Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Атрибуты и возвращаемые значения  (Прочитано 12240 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Dmitry
Помогающий

ru
Offline Offline

« : 16-09-2011 20:14 » 

Такой код:
Код:
[AttributeUsage(AttributeTargets.ReturnValue, Inherited = true)]
public class MyAttrAttribute : Attribute
{
        public string S { get; set; }
        public MyAttrAttribute(string s)
        {
            S = s;
        }
}

Код:
public class A
{
        [return: MyAttr("abc")]
        public virtual void Foo(int a)  { }
}

public class B : A
{
        public override void Foo(int a) { }
}

Теперь, если пишем
Код:
Type t = typeof(B);
MethodInfo mi = t.GetMethod("Foo");
ParameterInfo pi = mi.ReturnParameter;         
MyAttrAttribute atr = (MyAttrAttribute)Attribute.GetCustomAttribute(pi, typeof(MyAttrAttribute));
if (atr != null)
{
        Console.WriteLine(atr.S);
}

Attribute.GetCustomAttribute выбрасывает исключение IndexOutOfRange!
Если явно пометить атрибутом возвращаемое значение переопределённого метода (хотя в этом нет смысла - атрибут наследуемый)  - ничего не меняется. Ну а для класса A всё как и должно быть. И с параметрами методов. Т.е. не получается проверить на применение атрибутов возвращаемые значения только в переопределённых методах.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 17-09-2011 06:14 » 

Dmitry, ты же делаешь override метода - это уже другой метод (другая функция). Тот факт, что в таблице виртуальных функций она занимает место предыдущей, к твоему атрибуту не имеет никакого отношения. Атрибут принадлежит функции, а не позиции в таблице виртуальных функций. Убери override-метод или добавь атрибут к override-методу, и всё взлетит.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #2 : 17-09-2011 11:11 » 

Dimka, обрати внимание, что атрибут наследуемый - при определении класса атрибутов я указал явно Inherited = true (но этого можно и не делать, оно по умолчанию true).
Немного переписал пример до адекватного состояния.

Код:
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class MyAttrAttribute : Attribute
{
        public string S { get; set; }
        public MyAttrAttribute(string s)
        {
            S = s;
        }
}
Код:
 
public class A
{
        [MyAttr("abc")]
        public virtual void Foo(int a)
        {
        }
}

public class B : A
{
        public override void Foo(int a)
        {
        }
}

Код:
Type t = typeof(B);
MethodInfo mi = t.GetMethod("Foo");
MyAttrAttribute atr = (MyAttrAttribute)Attribute.GetCustomAttribute(mi, typeof(MyAttrAttribute), true);
if (atr != null)
{
      Console.WriteLine(atr.S);
}

Наследуемый атрибут применяем к виртуальному методу Foo базового класса. Теперь мы, допустим, хотим узнать, помечен ли атрибутом переопределённый метод в классе B. Метод Attribute.GetCustomAttribute смотрит метаданные метода в классе B, не найдя там экземпляра нашего атрибута, просматривает базовые классы (для этого третьим параметром в GetCustomAttribute для наглядности передаём true, можно вызвать без этого параметра, по-умолчанию всё равно true). И на выходе программы будет - "abc" -  всё работает.
С параметрами тоже работает. А с возвращаемым значением - нет. В любом случае по документации GetCustomAttribute не должен вызывать такое исключение. Явно баг.

Добавлено через 54 минуты и 59 секунд:
Убери override-метод или добавь атрибут к override-методу, и всё взлетит.
Если убрать override - заработает. Тогда по иерархии классов не надо проходиться.
Если добавить атрибут к override-методу - ничего не изменится, я об оэтом писал уже. Хотя GetCustomAttribute возвращает ссылку только на один экземпляр атрибута, он проверяет, не применялся ли атрибут несколько раз (тогда бросает исключение). Поэтому, если мы вызываем  GetCustomAttribute с параметром inherit = true для виртуальных методов, он вынужден пройтись по всем базовым классам.
« Последнее редактирование: 17-09-2011 12:06 от Dmitr » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 17-09-2011 18:32 » 

Код: (C#)
foreach(object o in typeof(B).GetMethod("Foo").GetCustomAttributes(true))
{
    MyAttribute a = o as MyAttribute;
    if(a != null)
    {
        Console.WriteLine(a.S);
    }
}
Однако, работает. Если ты, действительно, хочешь узнать, помечен ли метод класса твоим атрибутом, а не что-то другое.

Добавлено через 27 минут и 22 секунды:
Dmitry, я невнимательно прочитал, а ты меня запутал своими рассуждениями.

Ты хочешь пометить атрибутом не метод, а возвращаемое методом знание. Оно-то у тебя никак не наследуется. Наоборот, ты его заменяешь на новое, выполняя override. Наследуется лишь позиция в таблице виртуальных функций. Если не делать override, унаследуется и сам метод (как функция со своими параметрами и результатом). Но ты-то пишешь новый экземпляр функции со своими экземплярами параметров и возвращаемого значения. Ты хочешь функцию поменять, а атрибуты составных частей функции унаследовать - на каком основании? Как компилятор должен догадаться об этом? Слишком усложняешь семантику. Есть семантика базового и производного класса, но нет семантики базовой и производной функции (метода)!

И какой смысл помечать атрибутом возвращаемое значение, а не весь метод?
« Последнее редактирование: 17-09-2011 18:59 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #4 : 17-09-2011 20:04 » 

Для чего это нужно - это, конечно, хороший вопрос Улыбаюсь Наверное, на извращениях проще баг словить.
Но я не вижу тут никаких противоречий для компилятора или ещё чего-нибудь. С помощью reflection всё должно прекрасно выуживаться из метаданных, без размышлений о семантики и виртуальных таблицах. А раз уж метод Attribute.GetCustomAttributes принимает параметр ParameterInfo - так пусть ведёт себя с ним адекватно. Наследование атрибутов параметров ведь работает.

Вот, кстати, неспроста реализации методов GetCustomAttributes и IsDefined класса ParameterInfo игнорируют параметр Inherited, т.е. они не исследуют иерархию классов.
« Последнее редактирование: 17-09-2011 20:11 от Dmitry » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 18-09-2011 08:19 » 

Цитата: Dmitry
А раз уж метод Attribute.GetCustomAttributes принимает параметр ParameterInfo - так пусть ведёт себя с ним адекватно. Наследование атрибутов параметров ведь работает.
Ты здесь себе же противоречишь. Наследование атрибутов параметров при override метода не работает - именно потому, что для ParameterInfo не существует понятий "базовый" и "производный". И это адекватное поведение.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #6 : 18-09-2011 13:51 » 

Код: (C#)
 public class A
{
        public virtual void Foo([MyAttribute("param1")] int a) {}
}

public class B : A
{
        public override void Foo([MyAttribute("param2")] int a){}
}

Код: (C#)
ParameterInfo[] param = typeof(B).GetMethod("Foo").GetParameters();
foreach (object o in Attribute.GetCustomAttributes(param[0], true))
{
        MyAttribute a = o as MyAttribute;
        if (a != null)
        {
                Console.WriteLine(a.S);
        }
}

param2
param1

Почему я не могу ожидать такое же поведение для возвращаемых значений?
Я же не убеждаю тебя, что наследование должно работать. Для методов типа ParameterInfo не существует наследования, и в приведенном выше примере на выходе для них было бы только "param2". Ну и отлично. Но тогда методы класса Attribude должны вести себя аналогично (это элементарно, для ParametrInfo своя перегрузка этих методов - ParametrInfo не наследуется от MemberInfo), но не выбрасывать загадочное исключение IndexOutOfRangeException!
« Последнее редактирование: 18-09-2011 14:27 от Dmitry » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 18-09-2011 14:15 » 

Код: (C#)
    [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
    class A: Attribute
    {
        public string S { get; set; }
        public A(string s)
        {
            this.S = s;
        }
    }

    class X
    {
        public virtual object F([A("test")]object p)
        {
            return null;
        }
    }

    class Y: X
    {
        public override object F(object p)
        {
            return null;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            foreach(ParameterInfo p in typeof(Y).GetMethod("F").GetParameters())
            {
                foreach(object o in p.GetCustomAttributes(true))
                {
                    A a = o as A;
                    if(a != null)
                    {
                        Console.WriteLine(a.S);
                    }
                }
            }
            Console.ReadKey();
        }
    }
А такой код ничего не выводит. Если же использовать статический метод класса Attribute, то значение выводится для параметра функции, а для возвращаемого значения возникает исключительная ситуация выхода индекса за пределы диапазона значений.

Из чего можно сделать вывод, что лучше этим не пользоваться и ни на что не надеяться. Недокументированная фича или какие-то побочные эффекты.

Так чего же ты всё-таких хочешь: на трудную жизнь пожаловаться, известить мир о кознях Microsoft или решить какую-то свою задачу? Если последнее, то описания задачи так до сих пор и не было. Если первое или второе, то будто бы вопрос исчерпан.
« Последнее редактирование: 18-09-2011 14:22 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #8 : 18-09-2011 14:38 » new

А такой код ничего не выводит. Если же использовать статический метод класса Attribute, то значение выводится для параметра функции, а для возвращаемого значения возникает исключительная ситуация выхода индекса за пределы диапазона значений.
Я про это и писал. В частности, постом выше.
Цитата
Из чего можно сделать вывод, что лучше этим не пользоваться и ни на что не надеяться. Недокументированная фича или какие-то побочные эффекты.
А можно и багом назвать.
Цитата
Так чего же ты всё-таких хочешь: на трудную жизнь пожаловаться, известить мир о кознях Microsoft или решить какую-то свою задачу? Если последнее, то описания задачи так до сих пор и не было. Если первое или второе, то будто бы вопрос исчерпан.
Без конкретной задачи фича - не фича? Случайно наткнулся на это, поделился, заодно разобраться хотел. Только и всего.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines