Кисточка у художника может быть разной, а можно и совсем без кисточки. В С++ типы исчезали после трансляции, а вот в C# они не исчезают и могут быть использованы на этапе выполнения. С# — это располневшая на мелкософтах Дельфи и основная причина юзанья атрибутов с типами — PropertyGrid, бывший TValueListEditor. Ну а потом можно придти к не очевидному выводу, что атрибуты с типами — просто и удобно.
Раньше методы, поля и свойства класса были чисто феней промеж кодерами, можно было сказать метод, а можно — функция, можно — поле, а можно — просто переменная. Но коль скоро есть тип в памяти, то к нему можно обращаться, а обращаясь нужно использовать точные термины. От любого объекта можно получить его тип, а от типа можно получить имена полей, свойств и методов, а по именам можно получить значение поля — т.е. уже другой объект, или вызвать метод со всеми параметрами и возвращаемым значением.
Таким образом, мы получили альтернативный запутаистичный способ программирования, когда мы можем писать не сами переменные, а их строковые имена. Строковые имена можно менять.
Каждый объект имеет две ипостаси: с одной стороны он есть поле какого то типа, а с другой стороны он сам имеет свой тип, в котором могут быть разные поля. Типы на этапе выполнения изменить нельзя, кроме как обратиться по адресу в памяти хакерским методом.
У каждого типа есть список атрибутов, этот список может быть пуст, а может быть и полон. Вот тут и начинается самое интересное. Оказывается у каждого объекта свой собственный уникальный тип. Иначе говоря, две переменные типа int имеют два разных типа. Эти типы могут быть одинаковы, а могут быть и различны, и различаться они будут списком атрибутов. Понятно, что exe-шник не переполняется повторяющимися типами, реализация идет через ссылки, но в дырявом представлении уровня кодера это именно так.
У объектов никаких атрибутов нет, атрибуты только у типов. От каждого объекта мы можем получить его тип, а в типе найти атрибуты. Но коль скоро тип у каждого объекта уникальный, то можно сказать, что у объекта есть атрибуты. Однако можно так же сказать, что объект описан в некотором типе, а в этом типе к этому объекту так же приписаны атрибуты. Таким образом, у объекта есть два списка атрибутов – список по его собственному типу, и список по типу, в котором этот объект описан.
Атрибуты есть не только у полей, но и у методов, и у свойств. Чтобы использовать атрибуты их сперва нужно найти у объекта. Когда мы ищем атрибуты у объекта, то мы должны выбрать соответствующий список, а когда прописываем атрибуты – соответственно писать в тот тип, который следует. Зачастую кодер на всякий случай проверяет атрибуты и там и там, а при противоречии атрибутов отдает приоритет атрибуту поля перед атрибутом типа.
Первобытные кодеры полагали, что главное это прогу закодить. Потом предки пришли к тому, что главное это данные заструктурить. В наш просвещенный век универсальные проги бороздят просторы структурированного океана данных, наводя там новый порядок. Одна из таки программ – 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);
Раньше методы, поля и свойства класса были чисто феней промеж кодерами, можно было сказать метод, а можно — функция, можно — поле, а можно — просто переменная. Но коль скоро есть тип в памяти, то к нему можно обращаться, а обращаясь нужно использовать точные термины. От любого объекта можно получить его тип, а от типа можно получить имена полей, свойств и методов, а по именам можно получить значение поля — т.е. уже другой объект, или вызвать метод со всеми параметрами и возвращаемым значением.
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);