Лучший отвечающий
Наследование атрибутов

Вопрос
-
Здравствуйте.
Наверное, тему было бы правильнее назвать "Наследование наследованных атрибутов" :-)
.NET 3.5, C# 3: есть два класса-атрибута A1 и его наследник A2 . Есть два класса C1 и его наследник С2 , оба помеченные этими атрибутами. Примерно так:
using System; namespace AttributeInheritance { [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)] class A1Attribute : Attribute { public string Comment { get; set; } public override string ToString() { var name = GetType().Name.Replace("Attribute", string.Empty); return string.Format("{0} {1}", name, Comment); } } class A2Attribute : A1Attribute { } [A1(Comment = "#1 on C1")] [A1(Comment = "#2 on C1")] [A2(Comment = "#1 on C1")] [A2(Comment = "#2 on C1")] class C1 { } [A1(Comment = "#1 on C2")] [A1(Comment = "#2 on C2")] [A2(Comment = "#1 on C2")] [A2(Comment = "#2 on C2")] class C2 : C1 { } class Program { static void Main(string[] args) { WriteAttributes(typeof(C1)); WriteAttributes(typeof(C2)); Console.WriteLine("Press any key..."); Console.ReadKey(); } private static void WriteAttributes(Type type) { Console.WriteLine("=== Attributes of {0} class ===", type.Name); var items = type.GetCustomAttributes(true); Array.Sort(items, (x, y) => string.Compare(x.ToString(), y.ToString())); foreach (Attribute attr in items) Console.WriteLine(attr); Console.WriteLine("------------------------------"); Console.WriteLine(" {0} attribute(s)", items.Length); Console.WriteLine("=============================="); Console.WriteLine(); } } }
В результате запроса атрибутов для класса С2 , почему-то не возвращаются атрибуты A2 , примененные к классу-предку C1 , хотя атрибут A1 , а следовательно и атрибут A2 (учитывая что AttributeUsage сам помечен как наследуемый), являются наследуемыми и многократно используемыми:
=== Attributes of C1 class === A1 #1 on C1 A1 #2 on C1 A2 #1 on C1 A2 #2 on C1 ------------------------------ 4 attribute(s) ============================== === Attributes of C2 class === A1 #1 on C1 A1 #1 on C2 A1 #2 on C1 A1 #2 on C2 A2 #1 on C2 A2 #2 on C2 ------------------------------ 6 attribute(s) ==============================
Если не применять атрибут A2 к классу C2 , то атрибуты A2 , примененные к C1 попадают в список атрибутов:
[A1(Comment = "#1 on C2")] [A1(Comment = "#2 on C2")] /*[A2(Comment = "#1 on C2")] [A2(Comment = "#2 on C2")]*/ class C2 : C1 { }
=== Attributes of C2 class === A1 #1 on C1 A1 #1 on C2 A1 #2 on C1 A1 #2 on C2 A2 #1 on C1 A2 #2 on C1 ------------------------------ 6 attribute(s) ==============================
Таким образом, подтверждается, что атрибут A2 является наследуемым и многократно используемым (компилятор не запрещает применить его к одному и тому же классу дважды), то есть таким же, так же как атрибут A1 , но поведение Type.GetCustomAttribute для них существенно отличается...
Ситуацию можно исправить лишь явно разрешив множественное использование A2 :
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)] class A2Attribute : A1Attribute { }
=== Attributes of C2 class === A1 #1 on C1 A1 #1 on C2 A1 #2 on C1 A1 #2 on C2 A2 #1 on C1 A2 #1 on C2 A2 #2 on C1 A2 #2 on C2 ------------------------------ 8 attribute(s) ==============================
Таким образом, получается, что множественность использования переносится от атрибута-предка к атрибуту-наследнику каким-то странным половинчатым способом: множественное применение атрибутов разрешено (атрибут A2 применяется к классам С1 и C2 дважды), а правила наследования множественных атрибутов (когда атрибуты класса-предка должны добавляться к собственным атрибутам) почему-то нарушаются.
Вопрос: правильно ли я понимаю, что это ошибка в логике .NET, или кому-то такое поведение метода Type.GetCustomAttribute кажется правильным?
- Перемещено I.Vorontsov 27 мая 2010 г. 9:26 (От:Форум по .NET Framework)
- Изменено НТФ Град 27 мая 2010 г. 10:19 Съехало форматирование в коде
- Перемещено Siddharth Chavan 1 октября 2010 г. 21:26 MSDN Forums Consolidation (От:Visual C#)
27 мая 2010 г. 6:23
Ответы
-
Думаю, что я таки разобрался :-) Пришлось подебажить сорцы .NET Framework, но результат следующий: для наследников Multiple атрибутов свойство Inherited игнорируется. Такие вот дела. Таким образом, когда собираются custom attributes у класса C2, от класса C1 берутся только аттрибуты A1. В коде CustomAttribute.cs в функции AttributeUsageCheck есть коммент вида "Legacy: AllowMultiple ignored for none inheritable attributes." Скорее всего это поведение идет с одного из старых фреймворков.
В принципе, легко фиксится дублированием [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)] у C2.
Успехов,
Алекс
http://www.alexatnet.com/ - consulting, my blog, articles and discussions- Помечено в качестве ответа I.Vorontsov 2 июня 2010 г. 6:56
29 мая 2010 г. 1:03
Все ответы
-
Думаю, что я таки разобрался :-) Пришлось подебажить сорцы .NET Framework, но результат следующий: для наследников Multiple атрибутов свойство Inherited игнорируется. Такие вот дела. Таким образом, когда собираются custom attributes у класса C2, от класса C1 берутся только аттрибуты A1. В коде CustomAttribute.cs в функции AttributeUsageCheck есть коммент вида "Legacy: AllowMultiple ignored for none inheritable attributes." Скорее всего это поведение идет с одного из старых фреймворков.
В принципе, легко фиксится дублированием [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)] у C2.
Успехов,
Алекс
http://www.alexatnet.com/ - consulting, my blog, articles and discussions- Помечено в качестве ответа I.Vorontsov 2 июня 2010 г. 6:56
29 мая 2010 г. 1:03 -
Спасибо за ответ.
Legacy: AllowMultiple ignored for none inheritable attributes.
Получается, вроде как поняли, что что-то не так, но решили не изменять, для совместимости?
В принципе, легко фиксится дублированием [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)].
Ну да, только у меня наследников от A1 достаточно много (и вполне может быть еще добавятся), поэтому я поступил другим способом: сам бегу по иерархии наследования классов, запрашивая у каждого нужные атрибуты через Type.GetCustomAttribute(false) .
public static IEnumerable<Attribute> GetMultipleAttributes(Type attributeType, Type target) { if (target == null) throw new ArgumentNullException("target"); if (attributeType == null) throw new ArgumentNullException("attributeType"); do { foreach (Attribute item in target.GetCustomAttributes(attributeType, false)) yield return item; target = target.BaseType; } while (target != null); }
В принципе, тоже работает.31 мая 2010 г. 5:04 -
>Получается, вроде как поняли, что что-то не так, но решили не изменять, для совместимости?
Ну, это вопрос уже не ко мне :-) Но обычно Legacy обозначает именно это.
http://www.alexatnet.com/ - consulting, my blog, articles and discussions31 мая 2010 г. 13:37