Новое в рефлексии для .NET 4.5

Источник: habrahabr
bitmap

.NET 4.5 включает в себя некоторые изменения к System.Reflection. Самое значительно из них это то, что Type теперь разделён на два отдельных класса: Type и TypeInfo. Объект TypeInfo хранит в себе полное определение, а сам Type теперь хранит только общие данные. Если вы используете рефлексию из вашего десктопного или веб-приложения под NET 4.5, то старое API до сих пор тоже доступно наряду с новыми методами рефлексии. Сегодня я сфокусируюсь на том, как использовать некоторые из основных функций нового API.

Обзор Type и TypeInfo

Класс Type обеспечивает общее представление о структуре объекта. TypeInfo же представляет полное определение объекта, включая его связи с родительским и наследованными классами. Более того, API класса Type было обновлено и теперь возвращает коллекции в виде типизированных IEnumerable, а не как раньше - массивов. В случае больших и сложных сборок это позволяет читать значения результатов рефлексии по одному, а также упрощает использование метаданных типов при помощи LINQ. Приложения Windows Store имеют доступ только к этим новым IEnumerable коллекциям.

Изменения API класса Type

Давайте посмотрим на некоторые метаданные, которые могут быть получены из Type без использования TypeInfo, а затем погрузимся в API TypeInfo. API класса Type позволяет получать общую информацию о типах. Это означает, что вы можете получить такие метаданные как имя, пространство имён, полное имя, модуль и так далее. Это осуществляется через то же самое API как и в .NET 4.0. Например, чтобы получить полное имя и пространство имён некоторого типа, вы можете сделать следующее:

Type stringType = typeof(string);
string fullName = stringType.FullName;
string stringNameSpace = stringType.Namespace;

API TypeInfo

Как вы видите, Type обеспечивает общее представление о структуре класса. Если вы хотите погрузиться немного глубже, то вы можете использовать новое API класса TypeInfo. Значение TypeInfo вы можете получить из Type вызовом его метода GetTypeInfo(). Возьмём, например, класс Person, который содержит в себе свойства имени (FirstName), фамилии (LastName), событие Modified и метод Save:

public class Person 
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public event EventHandler Modified;
    public void Save()
    {
        // сохраняем объект в репозиторий...
    }
}

Теперь допустим, что вам нужно узнать какие свойства, методы и события определены в классе Person. Это можно легко узнать из TypeInfo при помощи его свойств DeclaredProperties, DeclaredMethods и DeclaredProperties:

TypeInfo personInfo = personType.GetTypeInfo();
IEnumerable<PropertyInfo> declaredProperties = personInfo.DeclaredProperties;
IEnumerable<MethodInfo> declaredMethods = personInfo.DeclaredMethods;
IEnumerable<EventInfo> declaredEvents = personInfo.DeclaredEvents;

Другой общей задачей рефлексии является необходимость найти все типы внутри сборки. При помощи обновленного API System.Reflection, класс Assembly теперь возвращает коллекцию TypeInfo вместо массива Type. Например чтобы получить все типы определённые в выполняемой сборке, вы можете использовать свойство TypeInfo.Assembly, а затем прочитать свойство DefinedTypes на полученном объекте Assembly:

Assembly myAssembly = this.GetType().GetTypeInfo().Assembly;
IEnumerable<TypeInfo> myTypes = myAssembly.DefinedTypes;

Чтобы получить лучшее представление о новых API System.Reflection, давайте создадим пример приложения, которое получает список всех типов в выполняемой сборке. А когда мы выбираем тип, мы будем отображать его имя, полное имя, свойства, методы и события.

Создаём приложение

Для начала создайте новое C# Windows Store приложение и добавьте туда класс для Person, как мы описывали раньше. Следующим шагом откройте  MainPage.xaml  и добаьте туда этот XAML в качестве корневого элемента Grid.

Теперь давайте обновим класс MainPage так, чтобы он собирал список типов, определённых в выполняемой сборке. Сначала добавьте using System.Reflectionв начало файла. Затем обновите метод OnNavigateTo, чтобы он получал полученные типы из сборки приложения и привязывал их к списку MyDefinedTypes:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    Assembly myAssembly = this.GetType().GetTypeInfo().Assembly;
    IEnumerable<TypeInfo> myTypes = myAssembly.DefinedTypes;
    MyDefinedTypes.DataContext = myTypes;
}

Теперь всё что нам осталось, так это связать событие SelectionChanged элементаMyDefinedTypes с обработчиком, который будет показывать выбранные TypeInfo в панели TypeInfoDetails:

private void MyDefinedTypes_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (e.AddedItems.Count > 0)
    {
        TypeInfo selectedTypeInfo = e.AddedItems.First() as TypeInfo;
        TypeInfoDetails.DataContext = selectedTypeInfo;
        TypeInfoDetails.Visibility = Windows.UI.Xaml.Visibility.Visible;
    }
}

В законченном виде ваш класс MainPage должен выглять так.

Поздравляю! Наше приложение готово и теперь выводит следующий результат во время выполнения:

Как вы видите, есть довольно значительные изменения в API класса Type. Это особенно важно, если вы переходите от предыдущих версий .NET и хотите создать приложение Windows Store, в котором доступно только новое API. Это API также доступно в обычных декстоп и веб-приложения .NET 4.5, а также в проектах Portable Class Library. Я полагаю, что новое API стало чище багодаря использованию IEnumerable вместо массивов, а также разделению общих и подробных данных рефлексии на два класса: Type и TypeInfo.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=31344