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

Original author: Eric Vogel
  • Translation
.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.
  • +24
  • 18.2k
  • 8
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 8

    +4
    Теперь в .net 4.5 сам черт ногу сломит как правильнее что-то сделать с рефлексией из-за смешивания старого и нового API.
      +2
      Хотелось бы знать, какая проблема решена данным разделением.
        +7
        Очевидно это влияет на производительность рефлексии.
        В тех случаях, когда нужны лишь основные данные о типе (имя, namespace,..) — процесс рефлексии ускоряется.
          0
          Не очень представляю, каким образом.

          Оставим пока в стороне вопрос, насколько нечасто информация о типе запрашивается только ради имени.
          Но даже если посмотреть на стандартную реализацию RuntimeType, то там кругом кеширование и ленивая инициализация для каждого из свойств. Так что как-то весьма сомнительно, что разбивкой функциональности по двум классам они сделали что-то более производительное, чем лукап объекта по GCHandle-у.

          Вот про IEnumerable вместо массивов — тут да, тут можно немного выиграть, но только если полный список членов никогда не нужен. Иначе разницы почти нет.
        +2
        А вы тоже рефлекшн называете рефлексией? Просто рефлексия — форма теоретической деятельности человека, направленная на осмысление своих собственных действий и их законов; деятельность самопознания, раскрывающая специфику духовного мира человека…
        Как-то в моём круге общения либо рефлекшн либо отражение… Но «рефлексия»???
          0
          Мне тоже слух режет, поэтому перед публикацией специально посмотрел как народ переводит.
          А между собой то тоже всегда рефлекшн, нэймспейс и эссембли.
            0
            Ну омонимов у нас предостаточно, одним больше — не страшно:) Просто «****шн» в русском языке звучит, на мой вкус, просто невыносимо. Так что «отражение», или можно транслитерировать как «рефлекция», что, кстати, этимологичнее, так как «-tion» — форма латинского «-tio», который «-цио».
            0
            мимо ответил

            Only users with full accounts can post comments. Log in, please.