none
Наследование атрибутов RRS feed

  • Вопрос

  • Здравствуйте.

    Наверное, тему было бы правильнее назвать "Наследование наследованных атрибутов" :-)

    .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#)

Ответы

  • Думаю, что я таки разобрался :-) Пришлось подебажить сорцы .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

Все ответы

  • Думаю, что я таки разобрался :-) Пришлось подебажить сорцы .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
  • Спасибо за ответ.
    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);
        }
    В принципе, тоже работает.

  • >Получается, вроде как поняли, что что-то не так, но решили не изменять, для совместимости?

    Ну, это вопрос уже не ко мне :-) Но обычно Legacy обозначает именно это.


    http://www.alexatnet.com/ - consulting, my blog, articles and discussions