Отражение (reflection) Отражение – получение из метаданных информации о типах, определенных в сборке. Классы, поддерживающие механизм отражения, находятся в пространстве имен System.Reflection. Класс System.Type – абстрактный класс для хранения информации о типе. Схема наследования для класса System.Type: System.Object System.Reflection.MemberInfo System.Type
Сборка и метаданные Сборка - исполняемое под управлением CLR приложение или библиотека с типами и/или ресурсами. Рисунок из MSDN Developer Tools \ Visual Studio 2008 \ Visual Studio \ Visual C# \ Getting Started with Visual C# \ Introduction to the C# Language and.NET Framework Метаданные содержат описание всех типов, определенных в приложении. Можно программно получить всю информацию о сборке, используя механизм отражения (reflection).
Метаданные Метаданные – это бинарная информация, которая добавляется в PE- файл (portable executable file) при компиляции исходного кода в MSIL. При выполнении кода CLR загружает метаданные в память. Метаданные содержат полную информацию о сборке. Описание сборки. Имя, версия, региональные стандарты (culture), ключ издателя (public key). Типы, определенные в сборке. Другие сборки, на которые ссылается данная сборка. Права (security permissions), необходимые для выполнения сборки. Описание каждого типа, определенного в приложении. Имя, видимость, базовый класс, реализованные интерфейсы. Члены класса (методы, свойства, поля, события, вложенные типы). Атрибуты – дополнительную декларативную информацию.
Метаданные -2 Методы классов, поддерживающих механизм отражения (reflection) ILDASM Сборщиком мусора При сериализации объекта VisualStudio для подсказок при написании исходного кода Метаданные доступны через Метаданные используются
Класс System.Type В классе System.Type определено более 100 свойств и методов, которые дают возможность получить полную информацию о типе. Объект типа System.Type, предоставляющий информацию о типе, уникален. Ссылку на объект System.Type для конкретного типа можно получить многими способами, но все ссылки на этот объект будут равны. Student st = new Student(); Type t1 = typeof(Student); Type t2 = st.GetType(); Console.WriteLine(object.ReferenceEquals(t1, t2)); // true Console.WriteLine("\ntypeof(Student) {0}", t1.FullName);
Некоторые свойства, определенные в классе System.Type public abstract class Type : MemberInfo, IReflect { public abstract Assembly Assembly {get;} // сборка, в которой определен // тип public TypeAttributes Attributes {get;} // атрибуты, связанные с типом public abstract Type BaseType {get;} // ссылка на базовый тип public abstract string FullName {get;} public bool IsSerializable {get;} public bool IsValueType {get;} public bool IsClass {get;} … }
Некоторые методы, определенные в классе System.Type public FieldInfo[ ] GetFields(); // информация об открытых полях типа; // есть перегруженная версия с возможностью настройки // на поля с другим типом доступа; public MethodInfo[ ] GetMethods(); public PropertyInfo[ ] GetProperties(); public abstract object[ ] GetCustomAttributes( bool inherit ); Типы FieldInfo, MethodInfo, PropertyInfo определены в пространстве имен System.Reflection и имеют свойства и методы для получения более детальной информации.
Как получить значение поля? Метод класса FieldInfo public abstract object GetValue( object obj ); дает возможность передать ссылку на инициализированный объект типа и получить значение поля. Например, Type tp = typeof(Student); Student stest = new Student(); FieldInfo[] flds = tp.GetFields(); if (flds.Length == 0) Console.WriteLine("\nNo public fields"); else { foreach (FieldInfo item in flds) { Console.Write(item.Name); Console.Write(" " + item.FieldType); Console.Write(" " + item.IsPrivate); object s = item.GetValue(stest); Console.Write(" " + s.ToString()); }
Вызов метода по ссылке на объект System.Type Метод класса MethodInfo дает возможность передать ссылку на инициализированный объект и значения параметров и выполнить метод. Например, public object Invoke( object obj, object[] parameters ); Type tp = typeof(Student); Student stest = new Student(); MethodInfo[] mds = tp.GetMethods(); foreach (MethodInfo item in mds) { Console.Write(item.Name); if (item.Name == "get_Group") { object[] parms = null; object ret = item.Invoke(stest, parms); Console.Write(" " + ret); }
Атрибуты В C# атрибуты используются для передачи декларативной информации от разработчика фрагментам кода. При компиляции эта информация добавляется к метаданным. Атрибуты предопределенные (из BCL) пользовательские (custom) В период выполнения атрибуты доступны с помощью механизма отражения (reflection). В C# атрибут размещается перед элементом, к которому он прикреплен. Следующие объявления эквивалентны [FlagsAttribute][SerializableAttribute] [Serializable][Flags] [Flags,Serializable]
Пользовательские атрибуты Пользовательский атрибут определяется путем определения класса, производного от System.Attribute. Атрибут может иметь позиционные и именованные параметры. Позиционные параметры являются обязательными и указываются при каждом использовании атрибута; порождаются конструкторами с типом доступа public. Именованные параметры не являются обязательными; значение параметра указывается вместе с именем параметра; всегда располагаются за позиционными; порождаются открытыми нестатическими полями и свойствами класса; Типами параметров атрибута могут быть только встроенные типы и одномерные sz-массивы этих типов. Значениями параметров могут быть только константные выражения.
Пример определения нового атрибута [AttributeUsage(AttributeTargets.Class)] public class MyCommentAttribute : Attribute {string last_update; string comment; public MyCommentAttribute(string last_update) {this.last_update = last_update; } public string Comment {get { return comment;} set { comment = value;} }
Атрибут AttributeUsage Используется только при определении классов атрибутов и может быть прикреплен только к классу, производному от System.Attribute. Параметры Позиционный – комбинация значений перечисления AttributeTargets ( определяет элементы, с которыми можно связать атрибут); Именованные bool AllowMultiple; – запрещает или разрешает многократное прикрепление к одному элементу ( умолчание false ); bool Inherited; – наследуется производным классом, если прикреплен к базовому ( умолчание true). Если при объявлении класса атрибута, атрибут AttributeUsage не указан, значение позиционного параметра считается равным AttributeTargets.All.
Перечисление AttributeTargets [SerializableAttribute] [FlagsAttribute] [ComVisibleAttribute(true)] public enum AttributeTargets All Assembly Class Constructor Delegate Enum Event Field GenericParameter Interface Method Module Parameter Property ReturnValue Struct Перечисление определяет элементы, с которыми можно связать атрибут. Элементы перечисления: Значения перечисления можно комбинировать с помощью побитовой операци OR.
Использование нового атрибута Объявляем класс и указываем атрибут [MyComment( ", Comment = "Class from Attributes_Demo")] [Serializable] public class Abc { …} Список атрибутов получаем при помощи метода GetCustomAttributes(). Значение true для второго параметра указывает, что атрибут может быть унаследован от базового класса. MemberInfo info = typeof (Abc); object [] ats1 = info.GetCustomAttributes(typeof(MyCommentAttribute),true);
Атрибут FlagsAttribute Атрибут Flags может быть прикреплен только к перечислению. [AttributeUsage(AttributeTargets.Enum)] [Serializable] public class FlagsAttribute : Attribute Атрибут указывает, что перечисление можно рассматривать как набор битовых флагов; значения перечисления можно комбинировать с помощью побитовой операци OR. [Flags] [Serializable] public enum FileAccess Примеры: [Flags] [Serializable] public enum AttributeTargets
Атрибут ConditionalAttribute - условная компиляция При помощи атрибута Conditional можно включать/выключать вызовы метода на этапе компиляции [AttributeUsage(AttributeTargets.Method)] [Serializable] public sealed class ConditionalAttribute : Attribute Параметр конструктора атрибута определяет символ (строку) условной компиляции: public ConditionalAttribute( string conditionString ); Атрибут Conditional используется в методах классов Debug и Trace. Visual Studio определяет символ TRACE для Release-компиляции. Visual Studio определяет символы TRACE и DEBUG для Debug- компиляции. Символ (строка) условной компиляции чувствителен к регистру. DEBUG и Debug – разные символы.
Атрибут ConditionalAttribute - 2 Вызов условного метода зависит от того, определен или нет символ условной компиляции (строка атрибута) в точке вызова метода. Если символ не определен, то не вычисляются параметры метода и вызов не включается в код. Ограничения на метод, к которому может быть прикреплен атрибут: метод класса или струтуры (но не интерфейса); возвращаемое значение должно быть void; метод не имеет модификатор override, но может иметь модификатор virtual; переопределенные версии этого метода (overrides) неявно получают атрибут Conditional; метод не может быть реализацией интерфейсного метода; нельзя использовать метод с атрибутом Conditional в delegate- creation-expression. Атрибут Conditional допускает многократное прикрепление (multiuse attribute ). Вызов метода включается в код, если определен хотя бы один из прикрепленных к методу символов условной компиляции.
Атрибут ConditionalAttribute - пример File Class1.cs Метод F класса Abc определен с атрибутом Conditional. Символ FAbc не определен. using System; using System.Diagnostics; namespace Conditional_Demo { class Class1 { static void Main(string[] args) { Abc abc = new Abc(); abc.F(); Console.WriteLine (1); Class2 cl2= new Class2(); cl2.F2(); } class Abc { [Conditional("FAbc")] public void F() {Console.WriteLine("Abc.F"); } }} #define FAbc using System; namespace Conditional_Demo { public class Class2 { public void F2() { Abc abc = new Abc(); abc.F(); } }} File Class2.cs. Символ FAbc определен. Вывод: 1 Abc.F