Содержание
Итак, в предыдущей части мы разобрались с тем, что из себя представляет рефлексия в C# и рассмотрели несколько вариантов получения типа System.Type
для исследования типов в C#. В этом части мы более детально рассмотрим свойства и методы класса Type
, используемые для изучения типов в C#.
Объект исследования
Для изучения будем использовать класс, разработанный в прошлой части:
class Person { public string Name { get; set; } public string Family { get; set; } private byte age; public byte Age { get { return age; } set { if (value > 100) throw new Exception("Возраст человека не может быть больше 100 лет"); else age = value; } } }
Получение и изучение членов класса
Для начала попробуем вывести всю информацию о типе, воспользовавшись методом GetMembers()
:
using System; using System.Reflection; namespace Reflection { class Person { public string Name { get; set; } public string Family { get; set; } private byte age; public byte Age { get { return age; } set { if (value > 100) throw new Exception("Возраст человека не может быть больше 100 лет"); else age = value; } } } class Program { static void Main(string[] args) { Person user = new Person { Name = "Вася", Family = "Пупкин", Age = 18 }; Type type = user.GetType(); foreach (MemberInfo info in type.GetMembers()) { Console.WriteLine($"{info.Name}, {info.DeclaringType}, {info.MemberType}"); } } } }
В приведенном примере мы получим все общедоступные члены класса Person
. Вывод консоли будет следующим:
set_Name, Reflection.Person, Method
get_Family, Reflection.Person, Method
set_Family, Reflection.Person, Method
get_Age, Reflection.Person, Method
set_Age, Reflection.Person, Method
GetType, System.Object, Method
ToString, System.Object, Method
Equals, System.Object, Method
GetHashCode, System.Object, Method
.ctor, Reflection.Person, Constructor
Name, Reflection.Person, Property
Family, Reflection.Person, Property
Age, Reflection.Person, Property
Свойство DeclaringType
возвращает полное название типа.
Свойство MemberType
возвращает значение из перечисления MemberTypes
, в котором определены следующие типы:
MemberTypes.Constructor
— член класса представляет собой конструкторMemberTypes.Method
— член класса представляет собой методMemberTypes.Field
— член класса представляет собой полеMemberTypes.Event
— член класса представляет собой событиеMemberTypes.Property
— член класса представляет собой свойствоMemberTypes.NestedType
— член класса представляет собой вложенный тип
Однако, на практике, нам чаще всего нет необходимости в получении информации вообще по всем членам класса. Наиболее часто нам может требоваться работать с определенными членами, например, отдельно с методами, свойствами и т.д. В этом случае применение метода GetMembers()
избыточно и достаточно воспользоваться специальными методами, определенными в Type
.
Получение информации о методах
Для получения информации о методах нам могут быть полезны методы GetMethods()
и GetParameters()
. Рассмотрим пример использования этих методов:
Person user = new Person { Name = "Вася", Family = "Пупкин", Age = 18 }; Type type = user.GetType(); foreach (MethodInfo method in type.GetMethods()) { string modificator = ""; if (method.IsStatic) modificator += "static"; if (method.IsVirtual) modificator += " virtual"; if (method.IsAbstract) modificator += " abstract"; string parameters = ""; foreach (ParameterInfo parameter in method.GetParameters()) { if (parameter.IsOut) parameters += "out "; parameters += $"{parameter.ParameterType.Name} {parameter.Name}"; } Console.WriteLine($"{modificator} {method.ReturnType.Name} {method.Name}({parameters}) "); }
Результат работы приложения:
Void set_Name(String value)
String get_Family()
Void set_Family(String value)
Byte get_Age()
Void set_Age(Byte value)
Type GetType()
virtual String ToString()
virtual Boolean Equals(Object obj)
virtual Int32 GetHashCode()
Как можно видеть по результатам работы программы, мы извлекли из нашего класса вообще все методы — и методы непосредственно самого класса и унаследованные методы и виртуальные и т.д. Вместе с этим мы можем воспользоваться переопределенной версией методы GetMethods()
, которая выглядит следующим образом:
MethodInfo[] GetMethods(BindingFlags)
Перечисление BindingFlags
может принимать следующие значения:
DeclaredOnly
— получает только методы непосредственно данного класса, унаследованные методы не извлекаютсяInstance
— получает только методы экземпляраNonPublic
— извлекает не публичные методыPublic
— получает только публичные методыStatic
— получает только статические методы
Объединяя значения с помощью побитовой операции ИЛИ можно комбинировать вывод. Например, получим только методы самого класса без унаследованных, как публичные, так и все остальные:
MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); foreach (MethodInfo method in methods) { string modificator = ""; if (method.IsStatic) modificator += "static"; if (method.IsVirtual) modificator += " virtual"; if (method.IsAbstract) modificator += " abstract"; string parameters = ""; foreach (ParameterInfo parameter in method.GetParameters()) { if (parameter.IsOut) parameters += "out "; parameters += $"{parameter.ParameterType.Name} {parameter.Name}"; } Console.WriteLine($"{modificator} {method.ReturnType.Name} {method.Name}({parameters}) "); }
В этом случае вывод консоли будет намного короче:
Void set_Name(String value)
String get_Family()
Void set_Family(String value)
Byte get_Age()
Void set_Age(Byte value)
Получение конструкторов
Для получения конструкторов применяется метод ConstructorInfo[] GetConstructors()
. Чтобы продемонстрировать работу этого метода допишем в нашем классе свой конструктор:
class Person { public Person(string name, string family, byte age) { Name = name; Family = family; Age = age; } [...] }
Type type = user.GetType(); foreach (ConstructorInfo ctor in type.GetConstructors()) { Console.Write(type.Name + " ("); // получаем параметры конструктора ParameterInfo[] parameters = ctor.GetParameters(); for (int i = 0; i < parameters.Length; i++) { Console.Write(parameters[i].ParameterType.Name + " " + parameters[i].Name); if (i + 1 < parameters.Length) Console.Write(", "); } Console.WriteLine(")"); }
Результат работы:
Person (String name, String family, Byte age)
Получение информации о полях и свойствах
Для извлечения полей и свойств применяются соответственно методы GetFields()
и GetProperties()
:
Type type = user.GetType(); Console.WriteLine("Поля класса:"); foreach (FieldInfo field in type.GetFields()) { Console.WriteLine($" {field.FieldType} {field.Name}"); } Console.WriteLine("Свойства класса:"); foreach (PropertyInfo prop in type.GetProperties()) { Console.WriteLine($" {prop.PropertyType} {prop.Name}"); }
Вывод консоли:
Свойства класса:
System.String Name
System.String Family
System.Byte Age
Опять же, воспользовавшись простой формой метода GetFields()
мы вывели только публичные поля. Чтобы получить все поля класса, мы можем, как в случае и с методами, воспользоваться переопределенной формой метода:
Console.WriteLine("Поля класса:"); foreach (FieldInfo field in type.GetFields(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly)) { Console.WriteLine($" {field.FieldType} {field.Name}"); }
В этом случае, вывод консоли будет следующим:
System.String k__BackingField
System.String k__BackingField
System.Byte age
Поиск реализованных интерфейсов
Чтобы получить все реализованные типом интерфейсы, надо использовать метод GetInterfaces()
, который возвращает массив объектов Type
:
Console.WriteLine("Реализованные интерфейсы:"); foreach (Type i in type.GetInterfaces()) { Console.WriteLine(i.Name); }
Так как каждый интерфейс представляет объект Type
, то для каждого полученного интерфейса можно также применить выше рассмотренные методы для извлечения информации о его свойствах и методах, а объединив все выше рассмотренные методы, можно провести комплексное изучение определенного типа и получить все его составные части.
Итого
Объект Type
позволяет проводить комплексное изучение определенного типа в C# — получать публичные и закрытые экземпляры класса (методы, поля, свойства), получать список наследуемых интерфейсов, изучать параметры методов и так далее. Комбинируя различные методы и свойства Type мы можем получать полную информацию о том или ином типе C#.