Как стать автором
Поиск
Написать публикацию
Обновить

С# aтрибуты и типы

Время на прочтение4 мин
Количество просмотров9.5K
Кисточка у художника может быть разной, а можно и совсем без кисточки. В С++ типы исчезали после трансляции, а вот в 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 — это такое волшебное слово, означающее обёртку со сборщиком мусора для неуправляемого вызова. Шарповцы полагают, что кодеры будут писать на шарпе и изредка подтягивать свои старые наработки на плюсах, а сами кодеры полагают, что они будут сопровождать своих монстров допиливая к ним кусочки на шарпе.

Коль скоро CCW может отправить в мусор то, что отправлять не следует, то в OLE версии все то, что потерялось нужно защищать static. Т.е. например потерялась последовательность properties в каком нибудь типе, все properties на месте, но не так отсортированы. Значит нужно по логике программы подняться выше и найти где еще ничего не потеряно и там создать static переменную со ссылкой на этот object — в мусор не отправят, постесняются.

Хакеры или не хакеры?

Сама концепция reflection чистой воды хакерство, когда мы в легкую обращаемся ко всем закрытым членам. Неизменяемость атрибутов на этапе выполнения, они де принадлежат типу, а тип изменить нельзя — такая же хакерская задачка как и все reflection. Все меняется. Вот только нужно помнить, что поменяв какую нибудь коллекцию можно не учесть то, что на нее кто то уже сослался и построил свою структуру данных — что то может и отвалиться. Например, меняем это мы значение атрибута Browsable — не дай бог у этот атрибут не прописан явно в исходнике. Этот атрибут, если его нет, приписывается к типу по умолчанию, но менять его нельзя, если он не прописан явно — посыплются все атрибуты Browsable у все других свойств. И определить где изначально быль прописан атрибут на этапе выполнения крайне сложно.

PropertyDescriptor pd = TypeDescriptor.GetProperties(_pi.DeclaringType)[«Имя»];
BrowsableAttribute ba = (BrowsableAttribute)pd.Attributes[typeof(BrowsableAttribute)];
FieldInfo isBrowsable = ba.GetType().GetField(«Browsable», BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance);
isBrowsable.SetValue(ba, false);
Теги:
Хабы:
Всего голосов 22: ↑7 и ↓15-8
Комментарии11

Публикации

Ближайшие события