Рефлексия (отражение) в C#. Работа со свойствами объекта (класс PropertyInfo)

При разработке программ в C# бывает необходимым определить имеется ли у экземпляра класса определенное свойство, проверить или записать его значение. Для выполнения этих и других операций мы можем воспользоваться классом из System.Reflection PropertyInfo. Этот класс предоставляет подробную информацию о свойстве класса или объекта.

Объект исследования

Допишем наш класс Person, используемый в прошлой статье, следующим образом:

class Account
 { 
     public string Login { get; set; }
     public string Password { get; set; } 
 }

 class Person
 {
     public Person()
     {
         Account = new Account();
     }
     public Person(string name, string family, byte age):this()
     {
         Name = name;
         Family = family;
         Age = age;
     }
     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;
                     
         }
     }
     public Account Account { get; private set; }
 }

Как можно увидеть, у нашего класса определено свойство Account, которое представляет собой т.н. вложенный тип.

Как найти определенное свойство класса в C#

Чтобы найти определенное свойство класса можно воспользоваться методом GetProperty(), например, следующим образом:

Person user = new Person("Вася", "Пупкин", 18);
user.Account.Login = "Uasya";
user.Account.Password = "password";

Type type = user.GetType();
PropertyInfo property = type.GetProperty("AcCoUnt", BindingFlags.IgnoreCase | BindingFlags.Instance| BindingFlags.Public);
if (property != null)
    Console.WriteLine("Свойство найдено");
else
    Console.WriteLine("Свойство не найдено");

Здесь следует обратить внимание на выставленные флаги BindingFlags. Используя связку BindingFlags.IgnoreCase | BindingFlags.Instance| BindingFlags.Public, мы пробуем найти публичное свойство, игнорируя регистр букв в имени. Поэтому результатом выполнения этого кода будет следующая строка:

Свойство найдено

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

Person user = new Person("Вася", "Пупкин", 18);
user.Account.Login = "Uasya";
user.Account.Password = "password";

Type type = user.GetType();
PropertyInfo property = type.GetProperty("AcCoUnt", BindingFlags.IgnoreCase | BindingFlags.Instance| BindingFlags.Public);
if (property != null)
{
    Console.WriteLine("Свойство найдено");
    Console.WriteLine($"Тип свойства: {property.PropertyType}");
    Console.WriteLine($"Доступно для чтения: {property.CanRead}");
    Console.WriteLine($"Доступно для записи: {property.CanWrite}");
    bool isClass = property.PropertyType.IsClass;
    Console.WriteLine($"Свойство является классом: {isClass}");
    if (isClass)
    {
        Console.WriteLine($"Перечисляем свойства класса {property.PropertyType.Name}");
        foreach (PropertyInfo info in property.PropertyType.GetProperties())
        {
            Console.WriteLine($"    {info.PropertyType.Name} {info.Name}");
        }
    }
}    
else
    Console.WriteLine("Свойство не найдено");

Результат работы:

Свойство найдено
Тип свойства: Reflection.Account
Доступно для чтения: True
Доступно для записи: True
Свойство является классом: True
Перечисляем свойства класса Account
String Login
String Password

Получив информацию о конкретном свойстве мы можем получить или записать его значение для указанного экземпляра (объекта).

Как получить значение свойства объекта в C#

Попробуем прочитать значение найденного свойства. Для этого воспользуемся методом GetValue() у PropertyInfo. Допишем наш пример следующим образом:

[...]
if (isClass)
{
    Console.WriteLine($"Перечисляем свойства класса {property.PropertyType.Name}");
    foreach (PropertyInfo info in property.PropertyType.GetProperties())
    {
        Console.WriteLine($"    {info.PropertyType.Name} {info.Name} Значение свойства: {info.GetValue(property.GetValue(user))}");
    }
}
[...]

Результат выполнения программы:

Свойство найдено
Тип свойства: Reflection.Account
Доступно для чтения: True
Доступно для записи: True
Свойство является классом: True
Перечисляем свойства класса Account
String Login Значение свойства: Uasya
String Password Значение свойства: password

Здесь стоит обратить внимание на то, как мы получили значение свойства. Для того, чтобы получить значение свойства мы должны обязательно передать в метод экземпляр (объект) исследуемого класса. В нашем случае, это объект с именем user. Таким образом, вначале мы использовали метод GetValue() для того, чтобы получить объект Account из объекта user и только затем мы передали значение свойства Account в метод GetValue(), чтобы прочитать значение свойств Login и Password.

Конечно, в реальном проекте мы можем столкнуться с ситуацией, когда свойство какого-либо класса само является классом, а этот класс содержит в себе свойство, которое также является классом и т.д., то есть вложенность объектов в исследуемом экземпляре может быть самая разная. В этом случае код приложения (если использовать способ приведенный выше) станет весьма и весьма запутанным. Но,можно воспользоваться следующим приемом, чтобы получить информацию о свойстве:

private static PropertyInfo GetProperty(object t, string PropertName)
{
    if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
        throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
    if (PropertName.Split('.').Length == 1)
        return t.GetType().GetProperty(PropertName);
    else
        return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}

В приведенном методе необходимо указать ссылку на экземпляр класса (объект) и имя свойства, которое необходимо найти, при этом вложенность свойства определяется точками, например:

PropertyInfo property = GetProperty(user, "Account.Login");

В данном случае, мы пробуем получить информацию о свойстве Login, которое относится к объекту Account, который, в свою очередь является свойством объекта user.

Как записать значение свойства объекта в C#

Для записи значения свойства нам необходимо воспользоваться методом SetValue(). Например:

Person user = new Person("Вася", "Пупкин", 18);
user.Account.Login = "Uasya";
user.Account.Password = "password";

Type type = user.GetType();
PropertyInfo property = type.GetProperty("Name");
if (property != null)
{
    Console.WriteLine($"Старое значение свойства {property.GetValue(user)}");
    property.SetValue(user, "Петя");
    Console.WriteLine($"Новое значение свойства {property.GetValue(user)}");
}

Итого

Для работы со свойствами классов и объектов C# используется класс PropertyInfo. Используя экземпляры этого класса мы можем получить детальную информацию о свойстве, а также прочитать или записать его значение для определенного экземпляра класса, используя, соответственно, методы GetValue() и SetValue().

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии