Учебное пособие (1077022), страница 22
Текст из файла (страница 22)
Для получения информации о типах разработчики языка C#могли создать отдельный языковой механизм. Но поскольку C# –объектно-ориентрованный язык, то и рефлексию типов разработчики языкаC# реализовали на основе объектно-ориентрованного подхода. Type – этоспециализированный класс, содержащий информацию о других классах.Объект класса Type содержит информацию о конкретном классе.Получить информацию о типе можно двумя способами. Если созданобъект класса, то получить информацию о классе можно с помощью179метода GetType. Метод GetType унаследован от класса Object, поэтому ондолжен присутствовать у любого объекта языка C#.Пример получения информации о типе с помощью метода GetType:ForInspection obj = new ForInspection();Type t = obj.GetType();Если объект класса не создан, то можно применить оператор typeof.Пример получения информации о типе с помощью оператора typeof:Type t = typeof(ForInspection);Пример получения информации о типе ForInspection с использованиемкласса Type:ForInspection obj = new ForInspection();Type t = obj.GetType();//другой способ//Type t = typeof(ForInspection);Console.WriteLine("\nИнформация о типе:");Console.WriteLine("Тип " + t.FullName + " унаследован от " +t.BaseType.FullName);Console.WriteLine("Пространство имен " + t.Namespace);Console.WriteLine("Находится в сборке " + t.AssemblyQualifiedName);Console.WriteLine("\nКонструкторы:");foreach (var x in t.GetConstructors()){Console.WriteLine(x);}Console.WriteLine("\nМетоды:");foreach (var x in t.GetMethods()){Console.WriteLine(x);}Console.WriteLine("\nСвойства:");foreach (var x in t.GetProperties()){Console.WriteLine(x);}Console.WriteLine("\nПоля данных (public):");foreach (var x in t.GetFields()){Console.WriteLine(x);}180Console.WriteLine("\nForInspection реализует IComparable -> " +t.GetInterfaces().Contains(typeof(IComparable)));Такимобразом,классTypeсодержитсвойстваиметоды,позволяющие получить информацию о конструкторах, методах, свойствах,полях данных исследуемого класса, проверить от какого классанаследуется класс, какие реализует интерфейсы.Результаты вывода в консоль:Информация о типе:Тип Reflection.ForInspection унаследован от System.ObjectПространство имен ReflectionНаходитсявсборкеReflection.ForInspection,Culture=neutral, PublicKeyToken=nullКонструкторы:Void .ctor()Void .ctor(Int32)Void .ctor(System.String)Методы:Int32 Plus(Int32, Int32)Int32 Minus(Int32, Int32)System.String get_property1()Void set_property1(System.String)Int32 get_property2()Void set_property2(Int32)Double get_property3()Int32 CompareTo(System.Object)System.String ToString()Boolean Equals(System.Object)Int32 GetHashCode()System.Type GetType()Свойства:System.String property1Int32 property2Double property3Reflection,Version=1.0.0.0,181Поля данных (public):Int32 field1Single field2ForInspection реализует IComparable -> TrueПо аналогии с конструкцией typeof в версии C# 6 появиласьконструкция nameof, которая возвращает имя объекта.
Это полезнаяособенность, так как при использовании рефлексии мы не всегда можемзнать имя объекта.Пример использования конструкции nameof:PropertyExample pe1 = new PropertyExample();Console.WriteLine(nameof(pe1));Результаты вывода в консоль:pe18.3 Динамические действия с объектами классовМетод InvokeMember класса Type позволяет выполнять динамическиедействия с объектами классов: создавать объекты, вызывать методы,получать и присваивать значения свойств и др.Его особенность заключается в том, что имена свойств, классовпередаются методу InvokeMember в виде строковых параметров.В следующем примере с использованием метода InvokeMemberсоздается объект класса ForInspection и вызывается его метод:Type t = typeof(ForInspection);Console.WriteLine("\nВызов метода:");//Создание объекта//ForInspection fi = new ForInspection();//Можно создать объект через рефлексиюForInspection fi =(ForInspection)t.InvokeMember(null, BindingFlags.CreateInstance,null, null, new object[] { });//Параметры вызова методаobject[] parameters = new object[] { 3, 2 };//Вызов методаobject Result =t.InvokeMember("Plus", BindingFlags.InvokeMethod,182null, fi, parameters);Console.WriteLine("Plus(3,2)={0}", Result);Метод InvokeMember принимает различные параметры: имя методаили свойста, к которому происходит обращение, список аргументоввызываемого метода и др.ОднимизважнейшихпараметровявляетсяпараметртипаBindingFlags – это перечисление, которое определяет выполняемоедействие: создание объекта, обращение к методу или свойству и т.д.Результаты вывода в консоль:Вызов метода:Plus(3,2)=58.4 Работа с атрибутамиРабота с атрибутами, также как и с типами данных – один из наиболеечасто применяемых механизмов рефлексии.Поскольку в языке C# рефлексия реализована с использованиемобъектно-ориентированного подхода, то атрибуты являются классами.Пример класса атрибута:using System;namespace Reflection{/// <summary>/// Класс атрибута/// </summary>[AttributeUsage(AttributeTargets.Property, AllowMultiple =false, Inherited = false)]public class NewAttribute : Attribute{public NewAttribute() { }public NewAttribute(string DescriptionParam){Description = DescriptionParam;}public string Description { get; set; }}}183Если класс применяется в качестве атрибута, то он должен бытьунаследован от класса System.Attribute.
Класс может содержать любыеданные и методы. В данном случае у класса есть два конструктора (спараметром и без параметра) и автоопределяемое свойство Description.Также класс атрибута должен быть, в свою очередь, помеченатрибутом AttributeUsage, который принимает три параметра: параметр типа перечисление AttributeTargets, которое указывает,к каким элементам класса может применяться атрибутNewAttribute (классам, свойствам, методам и т.д.); в данномслучае только к свойствам (Property); логический параметр AllowMultiple, указывающий, может липрименяться к свойству несколько атрибутов NewAttribute; вданном случае это запрещено; логический параметр Inherited, указывающий, наследуется лиатрибут классами, производными от класса с атрибутами;обычно используется значение false.Применять несколько атрибутов можно, например, в тех случаях,когда с их помощью помечают сделанные изменения, каждое изменениеможет помечаться атрибутом с указанием даты-времени и описанияизменения.Рассмотрим использование атрибута NewAttribute на примере классаForInspection.Фрагменткласса,которыйсодержитатрибутов:[NewAttribute("Описание для property1")]public string property1{get { return _property1; }set { _property1 = value; }}private string _property1;[NewAttribute(Description = "Описание для property3")]public double property3 { get; private set; }использование184Атрибутнеобходимозаписыватьвквадратныхскобкахнепосредственно перед тем элементом класса, к которому он относится; вданном случае атрибут применяется к свойствам.Каждая запись в квадратных скобках – это объект соответствующегокласса атрибута, в данном случае объект класса NewAttribute.
В примеремы «приклеиваем стикер» в виде объекта класса NewAttribute ксоответствующему свойству класса ForInspection, аннотируем свойстванекоторой дополнительной информацией.Присозданииопределяетавтоматическианнотацииконструкторыпревращаютсямеханизм IntelliSenseкласса-атрибута.вименованныеВсеавтоматическиpublic-свойствапараметры.Примериспользования IntelliSense для аннотации показан на рис. 22.Информация на рис. 22 полностью соответствует определению классаNewAttribute.Действительно,классNewAttributeсодержитдваконструктора (пустой и с одним строковым параметром), а также publicсвойство Description, которое IntelliSense определяет как именованныйпараметр аннотации.Рис.
22. Работа IntelliSense для аннотаций.При компиляции аннотации также компилируются и записываются всоответствующую сборку. Затем с помощью механизма рефлексии можнопроверить снабжен ли элемент класса какой-либо аннотацией. Врассматриваемом примере такая проверка вынесена в отдельную функцию:/// <summary>/// Проверка, что у свойства есть атрибут заданного типа/// </summary>/// <returns>Значение атрибута</returns>public static bool GetPropertyAttribute(PropertyInfo checkType, TypeattributeType, out object attribute){185bool Result = false;attribute = null;//Поиск атрибутов с заданным типомvar isAttribute =checkType.GetCustomAttributes(attributeType, false);if (isAttribute.Length > 0){Result = true;attribute = isAttribute[0];}return Result;}Метод принимает в качестве параметров информацию о проверяемомсвойстве «PropertyInfo checkType» и тип проверяемого атрибута «TypeattributeType».














