Avatar ·

Как получить список свойств класса в C#? Получение имён и типов свойств через рефлексию

Всем привет! Я только начал изучать C# и столкнулся с задачей: мне нужно программно получить список всех свойств (Property) какого-нибудь класса, например, чтобы вывести их имена и типы в консоль или сохранить в файл. Допустим, у меня есть такой класс: ```csharp public class Person { public string Name { get; set; } public int Age { get; set; } public string City { get; set; } private string secret; // это поле, не свойство } ``` Я хочу на выходе получить что-то вроде: - Name: string - Age: int - City: string Я слышал, что для этого используется рефлексия (Reflection), но не понимаю, как именно её применить. Пробовал гуглить, но нашёл много разных вариантов — с GetProperties(), BindingFlags и т.д. Подскажите, пожалуйста, самый простой и правильный способ получить список публичных свойств класса, а также их типов. Желательно с примером кода, который можно сразу запустить. Заранее спасибо! <<>>

Использование рефлексии для получения свойств класса

В C# для получения списка свойств класса используется пространство имён System.Reflection. Основной метод — Type.GetProperties(), который возвращает массив объектов PropertyInfo. Каждый такой объект содержит информацию об имени свойства, его типе, модификаторах доступа и многом другом.

Вот простейший пример, который выводит имена и типы всех публичных свойств класса Person:

using System;
using System.Reflection;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string City { get; set; }
    private string secret; // поле, не свойство
}

public class Program
{
    public static void Main()
    {
        // Получаем тип класса Person
        Type personType = typeof(Person);
        
        // Получаем все публичные свойства
        PropertyInfo[] properties = personType.GetProperties();
        
        Console.WriteLine("Свойства класса Person:");
        foreach (PropertyInfo prop in properties)
        {
            Console.WriteLine($"- {prop.Name}: {prop.PropertyType.Name}");
        }
    }
}

Вывод программы:

Свойства класса Person:
- Name: String
- Age: Int32
- City: String

Подробное объяснение

  • typeof(Person) — получает объект Type, который представляет метаданные класса.
  • GetProperties() — возвращает только публичные свойства (public). По умолчанию он не включает статические, защищённые или приватные свойства.
  • PropertyInfo — содержит свойства:
    • Name — имя свойства (строка).
    • PropertyType — тип свойства (объект Type).
    • CanRead/CanWrite — проверка наличия геттера/сеттера.

Фильтрация с помощью BindingFlags

Если нужно получить не только публичные, но и, например, статические или защищённые свойства, используйте перегрузку GetProperties(BindingFlags):

// Получить все свойства (публичные и непубличные, экземплярные и статические)
PropertyInfo[] allProperties = personType.GetProperties(
    BindingFlags.Public | BindingFlags.NonPublic | 
    BindingFlags.Instance | BindingFlags.Static);

// Только статические публичные свойства
PropertyInfo[] staticProperties = personType.GetProperties(
    BindingFlags.Public | BindingFlags.Static);

Получение значений свойств из объекта

Если у вас есть экземпляр класса, вы можете не только получить имена свойств, но и прочитать/записать их значения:

Person person = new Person { Name = "Анна", Age = 30, City = "Москва" };

foreach (PropertyInfo prop in personType.GetProperties())
{
    object value = prop.GetValue(person); // читаем значение
    Console.WriteLine($"{prop.Name} = {value}");
}

// Изменение значения
PropertyInfo ageProp = personType.GetProperty("Age");
ageProp.SetValue(person, 31);
Console.WriteLine($"Новый возраст: {person.Age}"); // 31

Важные замечания

  • Рефлексия работает медленнее прямого доступа к свойствам, поэтому не используйте её в горячих циклах.
  • Для сериализации/десериализации лучше использовать готовые библиотеки (Newtonsoft.Json, System.Text.Json).
  • Свойства, определённые как public string Name { get; set; } — это автоматические свойства. Рефлексия видит их как обычные свойства.

Метод GetProperties() — самый простой и часто используемый способ получить список свойств. Он идеально подходит для задач логирования, построения UI на лету или создания универсальных обработчиков данных.

<<>>

Альтернативный подход: использование Expression Trees и атрибутов

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

Пример: создадим атрибут [Displayable] и будем получать только свойства с ним:

[AttributeUsage(AttributeTargets.Property)]
public class DisplayableAttribute : Attribute { }

public class Person
{
    [Displayable]
    public string Name { get; set; }
    
    public int Age { get; set; } // без атрибута
    
    [Displayable]
    public string City { get; set; }
}

// Получение свойств с атрибутом
var properties = typeof(Person).GetProperties()
    .Where(p => p.GetCustomAttribute<DisplayableAttribute>() != null);

foreach (var prop in properties)
{
    Console.WriteLine($"{prop.Name} ({prop.PropertyType.Name})");
}
// Вывод: Name (String), City (String)

Другой вариант — использовать Expression Trees для строго типизированного доступа к свойству (без магических строк):

Expression<Func<Person, string>> expression = p => p.Name;
MemberExpression member = (MemberExpression)expression.Body;
Console.WriteLine(member.Member.Name); // "Name"

Этот подход применяется в ORM (например, Entity Framework) для указания свойств в запросах. Однако для простого получения списка всех свойств класса рефлексия с GetProperties() остаётся самым прямолинейным решением.