reflection

for Windows Store apps

Взаимодействие с системой типов в runtime






Автор: Виктор Лова / nsinreal



ЗачЕМ?


  1. Обобщение и сокращение кода
  2. Работа с неизвестными типами
  3. Нарушение инкапсуляции


ВОЗМОЖНОСТИ


Получить  абсолютно все данные о сборке, 
типе, методах, событиях, полях, 
свойствах, атрибутах и прочей ерунде.

Все полученные методы, 
геттеры/сеттеры свойств
можно выполнить.


ПРИМЕРЫ


  1. Сериализаторы/десериализаторы
  2. Data binding
  3. ORM-мапперы
  4. Mock-фреймворки
  5. Поиск нужных типов/методов
  6. Валидация данных в ASP.NET MVC


ИСПОЛЬЗОВАНИЕ

Получение типа


typeof(string)
Или
"abc".GetType()

В результате получаем объект типа System.Type.

SYSTEM.TYPE


В .NET 4.5 появилось разделение на Type и TypeInfo.

Класс Type обеспечивает общее представление о структуре объекта.

TypeInfo же представляет полное определение объекта, включая его связи с родительским и наследованными классами.

SYSTEM.TYPE


  1. Получение имени типа:
    "abc".GetType().FullName
  2. Получение имени сборки:           
    "abc".GetType().AssemblyQualifiedName
  3. Получение списка generic-параметров:
    "abc".GetType().GenericTypeArguments
  4. Проверка, можно ли создать экземпляр типа
    "abc".GetType().IsConstructedGenericType

SYSTEM.TYPE


  1. Получение  непараметризированного типа
    new List<int>().GetType().GetGenericTypeDefinition()
  2. Создание параметризированного типа:
    typeof(List<>).MakeGenericType(new [] { typeof(int) })
  3. Создание типа для двумерного массива
    typeof(string).MakeArrayType(2)

SYSTEM.TYPE


Поскольку System.Type немного кастрированный, то существуют extension-методы для него.

Для их использования нужно подключить namespace System.Reflection.

К примеру GetTypeInfo() возвращает TypeInfo для данного типа

SYSTEm.TYPE


Куча полезных extension методов определенны в классе RuntimeReflectionExtensions

Позволяют получить информацию о событиях, полях, свойствах и методах.

Соответствующие методы возвращают все члены класса, включая отнаследованные, непубличные, экземплярные и статические

SYSTEM.TYPE

Extensions


Общий паттерн для получения метаданных

Получить определенной член типа:
GetRuntime[...](string name)

Получить все члены типа:
GetRuntime[...]s()


Создание ЭКЗЕМПЛЯРА


Честное получение конструктора:
typeof(List<int>)  .GetTypeInfo()
.DeclaredConstructors
.First()
.Invoke(new object[] { })

Более простой способ:
 Activator.CreateInstance(typeof(List<int>))

System.Reflection.Typeinfo


Содержит кучу свойств, которые возвращают
IEnumerable<Метаданные> 

Свойства возвращают только то, что определено в самом типе. Если вам нужно получить, к примеру, все методы типа, то вам предлагается на выбор два варианта:

  • Вручную бегать по иерархии классов
  • Использовать GetRuntimeMethods()

SYSTEm.REFLECTION.TYPEINFO


Что можно получить:
  1. Атрибуты (CustomAttributes)
  2. Конструкторы (DeclaredConstructors)
  3. События (DeclaredEvents)
  4. Поля (DeclaredFields)
  5. Методы (DeclaredMethods)
  6. Свойства (DeclaredProperties)
  7. Вложенные типы (DeclaredNestedTypes)
  8. Вообще все члены типа (DeclaredMembers)
  9. Реализованные интерфейсы (ImplementedInterfaces)

SYSTEM.REFLECTION.TYPEINFO


Если нам нужны не все свойства, а только определенное, то для этого можно использовать метод GetDeclaredProperty().

Аналогичные методы есть для всего остального.

Для методов есть GetDeclaredMethod() и GetDeclaredMethods().  Второй вариант нужен из-за того, что у методов существуют перегрузки. 

System.Reflection.Typeinfo




Проверка на то, можно ли присвоить переменной одного типа значение другого типа:
typeof(object)
  .GetTypeInfo()
  .IsAssignableFrom(typeof(string).GetTypeInfo())

Атрибуты


В классе CustomAttributeExtensions определено пара полезных extension-методов для работы с атрибутами IsDefined, GetCustomAttribute, GetCustomAttributes.

Для каждого из них есть перегрузки:
  • Принимающие тип как аргумент метода
  • Принимающие тип как generic-параметр

Плюс для каждой перегрузки есть еще одна, принимающая булевое значение, говорящее о том, что нужно смотреть и на атрибуты предков.

Иерархия метаданных


  • MemberInfo
    • EventInfo
    • FieldInfo
    • MethodBase
      • ConstructorInfo
      • MethodInfo
    • PropertyInfo
    • Type
  • ParameterInfo


    ПОЛЯ


    • FieldType - тип поля
    • IsInitOnly - помечен ли readonly
    • IsLiteral - является ли константой
    • IsStatic - является ли  статическим членом
    • GetValue(object), SetValue(object, value)


    СВОЙСТВА


    • CanRead, CanWrite - проверка на то, можно ли из свойства читать/писать.
    • GetIndexParameters() - параметры для индексаторов
    • GetMethod, SetMethod - получение метаданных методов get/set
    • GetValue(...), SetValue(...) - простой способ изменить значение свойства. Есть перегрузки для индексаторов.



    События


    • AddMethod - метод подписки
    • RemoveMethod - метод отписки
    • RaiseMethod - метод вызова
    • EventHandlerType

    СОБЫТИЯ


    Увы, RaiseMethod бесполезен.
     Он всегда возвращает null.
    Но можно использовать такой хак:

    var eventInvoker = this.GetType().GetTypeInfo()
                           .GetDeclaredField("PropertyChanged")
                           .GetValue(this) as Delegate;
    
    if (eventInvoker != null)
    {
        eventInvoker.DynamicInvoke(this,                                new PropertyChangedEventArgs("Name"));
    } 

    МЕТОДЫ


    • ReturnType
    • ContainsGenericParameters
    • GetGenericArguments()
    • GetGenericMethodDefinition()
    • MakeGenericMethod(Type[] arguments)
    • GetParameters()
    • Invoke(object @object, object[] parameters)
      • Если первый параметр null, то вызов считается статическим

    Members


    У MemberInfo есть свойство Name.
    Практически у всех его наследников есть свойства:

    • для определения модификаторов доступа
    • статический/нестатический
    • IsSpecialName - определяет специальные члены, используемые самим языком (индексаторы и прочее)
    • для методов и свойств: IsAbstract, IsVirtual, IsFinal.  


    ПАРАМЕТРЫ МЕТОДОВ


    • HasDefaultValue, DefaultValue
    • IsOut - out, IsIn - ref
    • Position
    • ParameterType


    СКОРОСТЬ


    Естественно, рефлексия не бесплатна.

    Если постоянно дергать систему типов, то скорость приложения по сравнению с обычным кодом может проседать в 1000 раз.

    Если закешировать метаданные об определенном свойстве, то скорость проседает в 3-4 раза.

    Reflection for Windows Store Apps

    By Viktor Lova

    Reflection for Windows Store Apps

    • 1,717