Кисточка у художника может быть разной, а можно и совсем без кисточки. В С++ типы исчезали после трансляции, а вот в C# они не исчезают и могут быть использованы на этапе выполнения. С# — это располневшая на мелкософтах Дельфи и основная причина юзанья атрибутов с типами — PropertyGrid, бывший TValueListEditor. Ну а потом можно придти к не очевидному выводу, что атрибуты с типами — просто и удобно.

Раньше методы, поля и свойства класса были чисто феней промеж кодерами, можно было сказать метод, а можно — функция, можно — поле, а можно — просто переменная. Но коль скоро есть тип в памяти, то к нему можно обращаться, а обращаясь нужно использовать точные термины. От любого объекта можно получить его тип, а от типа можно получить имена полей, свойств и методов, а по именам можно получить значение поля — т.е. уже другой объект, или вызвать метод со всеми параметрами и возвращаемым значением.

class X
    {
        class A
        {
            public int f1;
            public int p1 { get; set; }
            public int m1(string s, int i) { return (0); }
        }
        X()
        {
            A a = new A();
            Type t = a.GetType();

            FieldInfo fi = t.GetField("f1");
            object of = fi.GetValue(a);

            PropertyInfo pi = t.GetProperty("p1");
            MethodInfo mpi = pi.GetGetMethod();
            object ompi = mpi.Invoke(a, null);

            MethodInfo mi = t.GetMethod("m1");
            object om = mi.Invoke(a, new object[] { "param1", 2 });
        }
    }

Таким образом, мы получили альтернативный запутаистичный способ программирования, когда мы можем писать не сами переменные, а их строковые имена. Строковые имена можно менять.

Каждый объект имеет две ипостаси: с одной стороны он есть поле какого то типа, а с другой стороны он сам имеет свой тип, в котором могут быть разные поля. Типы на этапе выполнения изменить нельзя, кроме как обратиться по адресу в памяти хакерским методом.

У каждого типа есть список атрибутов, этот список может быть пуст, а может быть и полон. Вот тут и начинается самое интересное. Оказывается у каждого объекта свой собственный уникальный тип. Иначе говоря, две переменные типа int имеют два разных типа. Эти типы могут быть одинаковы, а могут быть и различны, и различаться они будут списком атрибутов. Понятно, что exe-шник не переполняется повторяющимися типами, реализация идет через ссылки, но в дырявом представлении уровня кодера это именно так.

    class X
    {
        class ATTR1 : Attribute { public ATTR1() { } }
        class ATTR2 : Attribute { public ATTR2() { } }
        class A
        {
            [ATTR1]
            public int i1;

            [ATTR2]
            public int i2;

        }
        X()
        {

            object[] ai1 = a.GetType().GetField("i1").GetCustomAttributes(false);
            object[] ai2 = a.GetType().GetField("i2").GetCustomAttributes(false);
        }
    }

У объектов никаких атрибутов нет, атрибуты только у типов. От каждого объекта мы можем получить его тип, а в типе найти атрибуты. Но коль скоро тип у каждого объекта уникальный, то можно сказать, что у объекта есть атрибуты. Однако можно так же сказать, что объект описан в некотором типе, а в этом типе к этому объекту так же приписаны атрибуты. Таким образом, у объекта есть два списка атрибутов – список по его собственному типу, и список по типу, в котором этот объект описан.

    class X
    {
        class ATTR1 : Attribute { public ATTR1() { } }
        class ATTR2 : Attribute { public ATTR2() { } }

        [ATTR1]
        public class A { }

        [ATTR2]
        public A a;
        
        X()
        {
            a = new A();

            object[] inner = a.GetType().GetCustomAttributes(false);
            object[] outer = this.GetType().GetField("a").GetCustomAttributes(false);
        }
    }

Атрибуты есть не только у полей, но и у методов, и у свойств. Чтобы использовать атрибуты их сперва нужно найти у объекта. Когда мы ищем атрибуты у объекта, то мы должны выбрать соответствующий список, а когда прописываем атрибуты – соответственно писать в тот тип, который следует. Зачастую кодер на всякий случай проверяет атрибуты и там и там, а при противоречии атрибутов отдает приоритет атрибуту поля перед атрибутом типа.

Первобытные кодеры полагали, что главное это прогу закодить. Потом предки пришли к тому, что главное это данные заструктурить. В наш просвещенный век универсальные проги бороздят просторы структурированного океана данных, наводя там новый порядок. Одна из таки программ – PropertyGrid, которая показывает на экране дерево данных с возможностью их изменить. Как найдет public property – так сразу и покажет. А если property такое нестандартное, что его ни посмотреть, ни отредактировать, то PropertyGrid поищет атрибутик с описанием класса, которым можно посмотреть и поправить property. А если не найдет – то и показывать не станет. А че, СУБД давно хранят типы своих данных и живут припеваючи, почему бы и проге их не хранить – памяти на компах теперь складывать некуда, а на скоростях можно и неуправляемый кусочек закодить.

Известные глюки.

Усё работает внутри c#, но стоит выставить в OLE (COM, ActiveX), как сразу теряются некоторые атрибуты, скорее всего при переходе от одной c# dll к другой. Например когда сама полянка (field) описана в одной dll, а ее тип с атрибутом — в другой, при чем атрибут то не пропадет, пропадут его полянки — он же тоже класс в свою очередь. Трудно определить где конкретно глюк. Вероятнее всего CCW вальтует. CCW — это такое волшебное слово, означающее обёртку со сборщиком мусора для неуправляемого вызова. Шарповцы полагают, что кодеры будут писать на шарпе и изредка подтягивать свои старые наработки на плюсах, а сами кодеры полагают, что они будут сопровождать своих монстров допиливая к ним кусочки на шарпе.

Комментарии (11)


  1. lair
    23.09.2015 11:33
    +12

    Феерическая терминологическая каша.

    основная причина юзанья атрибутов с типами — PropertyGrid, бывший TValueListEditor.

    Нет, основная причина сохранения информации о типах вплоть до времени выполнения — это то, что код управляемый, а для управления нужна информация. Ну и reflection во всех его проявлениях — очень удобен.

    Каждый объект имеет две ипостаси: с одной стороны он есть поле какого то типа, а с другой стороны он сам имеет свой тип, в котором могут быть разные поля.

    Нет. Каждый объект — это объект (определенного типа, конечно же). Он не поле, он объект. Поле может содержать ссылку на объект (для ссылочных типов), или сам объект (для типов-значений при определенных условиях).

    Оказывается у каждого объекта свой собственный уникальный тип. Иначе говоря, две переменные типа int имеют два разных типа.

    А вот и нет. Они имеют один и тот же тип — typeof(int).

    class X
        {
            class ATTR1 : Attribute { public ATTR1() { } }
            class ATTR2 : Attribute { public ATTR2() { } }
            class A
            {
                [ATTR1]
                public int i1;
    
                [ATTR2]
                public int i2;
    
            }
            X()
            {
    
                object[] ai1 = a.i1.GetType().GetCustomAttributes(false);
                object[] ai2 = a.i2.GetType().GetCustomAttributes(false);
            }
        }
    

    Оба вызова вернут одно и то же. А чтобы получить [ATTR1] и [ATTR2], надо запрашивать атрибуты у FieldInfo.

    атрибуты только у типов.

    Тоже нет. Атрибуты есть у типов, у членов типов, у параметров и возвращаемых значений методов и даже у сборок.

    Чтобы использовать атрибуты их сперва нужно найти у объекта.

    А вот у объектов никаких атрибутов нет.


    1. whitepen
      23.09.2015 14:51
      -5

      Пример поправил. Строгая терминология полезна тем, кто знает C#, но им совершенно бесполезна моя статья. А вот тем, кто не знает терминов шарпа, а писал на плюсах, например, в самый раз. Вы наверное изволили заметить, что статья весьма коротка и не включает ничего. Зато писать уже можно.


      1. lair
        23.09.2015 14:54

        Пример поправил.

        … и как же вы теперь обосновываете свое заявление «две переменные типа int имеют два разных типа»?

        А вот тем, кто не знает терминов шарпа, а писал на плюсах, например, в самый раз.

        Зачем загаживать людям мозги ошибочной информацией?

        Зато писать уже можно.

        Но не нужно.


        1. whitepen
          23.09.2015 15:03
          -5

          Писать совершенные, взвешенные и продуманные суждения означает не писать ничего и никогда. Так устроен этот мир.

          Атрибут принадлежит типу, и если типы различаются списком атрибутов — значит они различны. Здесь кроется большая опасность, так как нужно думать когда атрибут выполняется — он ведь исполняемый класс в отличие от типа. Полагаю что шарповцы не смогли исключить все ошибки и далее не смогут.


          1. lair
            23.09.2015 15:13

            Атрибут принадлежит типу

            Да нет же, с чего вы это взяли? Атрибут может принадлежать как типу, так и члену типа, параметру и возвращаемому значению, и сборке целиком.

            если типы различаются списком атрибутов — значит они различны

            А где вы видите, что типы различаются списком атрибутов? Покажите конкретный пример кода и его вывод.


            1. whitepen
              23.09.2015 15:27
              -6

              Экий вы право же продвинутый. Я сознательно написал «переменная», что вообще говоря в терминах шарпа совсем никак. А потом долго описывал какие то ипостаси, которые так же никак, и связывал объект с типом внешним и внутренним на этапе выполнения, хотя нет ни внешних, ни внутренних типов. И писал про дырявые представления — т.е. изначально неверные представления. Чисто для понимания, а вовсе не для документирования. А вы, как я полагаю, решили блеснуть глубиной знаний шарпа, не так ли?


              1. lair
                23.09.2015 15:31

                Я сознательно написал «переменная», что вообще говоря в терминах шарпа совсем никак.

                Почему «никак»? В C# есть переменные.

                Чисто для понимания, а вовсе не для документирования.

                Не надо «для понимания» писать изначально неверные вещи.


                1. whitepen
                  24.09.2015 09:52
                  -3

                  О, да вы, батенька, крупный троль. Только вот увидал. Извините за недоброкачественные продукты питания.


          1. Alvaro
            23.09.2015 15:28

            Тип int никак не изменился от того, что у поля класса A есть атрибут ATTR1


  1. Karroplan
    23.09.2015 11:38
    +6

    это прорыв udaff.com на хабр?


    1. whitepen
      23.09.2015 14:56
      -4

      Конечно, исходник проги однозначно ее определяет, но вот прочитать его никак нельзя, если он чуть больше страницы. Для описания программы поневоле приходится использовать литературно-художественные понятия, такие как виртуальность, метод доступа, драйвер, интерфейс… которые вообще говоря ничего определенного не обозначают, или обозначают все что угодно.