reflection
for Windows Store apps
Взаимодействие с системой типов в runtime
ЗачЕМ?
-
Обобщение и сокращение кода
- Работа с неизвестными типами
- Нарушение инкапсуляции
ВОЗМОЖНОСТИ
Получить абсолютно все данные о сборке,
типе, методах, событиях, полях,
свойствах, атрибутах и прочей ерунде.
Все полученные методы,
геттеры/сеттеры свойств
можно выполнить.
ПРИМЕРЫ
- Сериализаторы/десериализаторы
- Data binding
- ORM-мапперы
- Mock-фреймворки
-
Поиск нужных типов/методов
- Валидация данных в ASP.NET MVC
ИСПОЛЬЗОВАНИЕ
Получение типа
SYSTEM.TYPE
В .NET 4.5 появилось разделение на Type и TypeInfo.
Класс Type обеспечивает общее представление о структуре объекта.
TypeInfo же представляет полное определение объекта, включая его связи с родительским и наследованными классами.
SYSTEM.TYPE
- Получение имени типа:
"abc".GetType().FullName
- Получение имени сборки:
"abc".GetType().AssemblyQualifiedName
- Получение списка generic-параметров:
"abc".GetType().GenericTypeArguments
- Проверка, можно ли создать экземпляр типа
"abc".GetType().IsConstructedGenericType
SYSTEM.TYPE
- Получение непараметризированного типа
new List<int>().GetType().GetGenericTypeDefinition()
- Создание параметризированного типа:
typeof(List<>).MakeGenericType(new [] { typeof(int) })
- Создание типа для двумерного массива
typeof(string).MakeArrayType(2)
SYSTEM.TYPE
Поскольку System.Type немного кастрированный, то существуют extension-методы для него.
Для их использования нужно подключить namespace System.Reflection.
К примеру GetTypeInfo() возвращает TypeInfo для данного типа
SYSTEm.TYPE
Позволяют получить информацию о событиях, полях, свойствах и методах.
Соответствующие методы возвращают все члены класса, включая отнаследованные, непубличные, экземплярные и статические
SYSTEM.TYPE
Extensions
Общий паттерн для получения метаданных
Получить определенной член типа:
GetRuntime[...](string name)
Получить все члены типа:
Создание ЭКЗЕМПЛЯРА
Честное получение конструктора:
typeof(List<int>) .GetTypeInfo()
.DeclaredConstructors
.First()
.Invoke(new object[] { })
Более простой способ:
Activator.CreateInstance(typeof(List<int>))
System.Reflection.Typeinfo
Содержит кучу свойств, которые возвращают
IEnumerable<Метаданные>
Свойства возвращают только то, что определено в самом типе. Если вам нужно получить, к примеру, все методы типа, то вам предлагается на выбор два варианта:
- Вручную бегать по иерархии классов
- Использовать GetRuntimeMethods()
SYSTEm.REFLECTION.TYPEINFO
Что можно получить:
- Атрибуты (CustomAttributes)
- Конструкторы (DeclaredConstructors)
- События (DeclaredEvents)
- Поля (DeclaredFields)
- Методы (DeclaredMethods)
- Свойства (DeclaredProperties)
- Вложенные типы (DeclaredNestedTypes)
- Вообще все члены типа (DeclaredMembers)
- Реализованные интерфейсы (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 раза.