← Назад к вопросам

Что такое рефлексия?

2.0 Middle🔥 101 комментариев
#Основы C# и .NET

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Рефлексия в C#

Рефлексия (Reflection) — это один из самых мощных, но и одновременно самых опасных инструментов в .NET. Я видел код, где неправильное использование рефлексии затухала производительность приложения в 100 раз. Но когда она используется правильно, это чудо техники.

Определение

Рефлексия — это способность программы во время выполнения изучать информацию о типах, методах, свойствах, конструкторах и других элементах сборки. Это позволяет динамически создавать объекты, вызывать методы и изменять значения, которые вы знаете только во время выполнения.

Основной концепт

using System.Reflection;

var person = new Person { Name = "Ivan", Age = 30 };

// Получаем информацию о типе
Type personType = person.GetType();  // Type = Person

// Читаем информацию о типе
Console.WriteLine(personType.Name);           // "Person"
Console.WriteLine(personType.Namespace);      // "MyApp"
Console.WriteLine(personType.BaseType.Name);  // "Object"

Основные возможности рефлексии

1. Получение информации о типах

var type = typeof(Person);

// Все публичные свойства
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in properties)
{
    Console.WriteLine($"{prop.Name}: {prop.PropertyType}");
    // Output:
    // Name: System.String
    // Age: System.Int32
}

// Все методы
var methods = type.GetMethods();

// Все конструкторы
var constructors = type.GetConstructors();

2. Динамическое создание объектов

// Создание объекта через рефлексию
var type = Type.GetType("MyApp.Person");
object person = Activator.CreateInstance(type);  // new Person()

// С параметрами конструктора
object person = Activator.CreateInstance(type, "Ivan", 30);

3. Динамическое чтение и запись свойств

var person = new Person { Name = "Ivan", Age = 30 };
var type = person.GetType();

// Чтение свойства
var nameProperty = type.GetProperty("Name");
var name = nameProperty.GetValue(person);  // "Ivan"

// Запись свойства
nameProperty.SetValue(person, "Pavel");
Console.WriteLine(person.Name);  // "Pavel"

4. Динамическое вызывание методов

var person = new Person();
var type = person.GetType();

// Получаем метод
var method = type.GetMethod("GetInfo");

// Вызываем его
object result = method.Invoke(person, null);  // Вызов GetInfo()
Console.WriteLine(result);  // Результат метода

5. Проверка наличия атрибутов

[Serializable]
public class Person
{
    [Required]
    public string Name { get; set; }
}

// Проверка атрибутов класса
var type = typeof(Person);
bool isSerializable = type.IsDefined(typeof(SerializableAttribute));

// Проверка атрибутов свойства
var nameProperty = type.GetProperty("Name");
bool isRequired = nameProperty.IsDefined(typeof(RequiredAttribute));

Реальные примеры использования

Пример 1: Сериализация JSON

public static string SerializeToJson(object obj)
{
    var type = obj.GetType();
    var properties = type.GetProperties();
    
    var pairs = new List<string>();
    foreach (var prop in properties)
    {
        var value = prop.GetValue(obj);
        var json = value is string ? $"\"{value}\"" : value;
        pairs.Add($"\"{prop.Name}\":{json}");
    }
    
    return "{" + string.Join(",", pairs) + "}";
}

var person = new Person { Name = "Ivan", Age = 30 };
var json = SerializeToJson(person);
// Output: {"Name":"Ivan","Age":30}

Пример 2: Автоматическое маппирование объектов

public static TTarget Map<TSource, TTarget>(TSource source)
    where TTarget : new()
{
    var target = new TTarget();
    var sourceType = source.GetType();
    var targetType = typeof(TTarget);
    
    // Копируем все свойства с одинаковым именем
    var properties = sourceType.GetProperties();
    foreach (var sourceProp in properties)
    {
        var targetProp = targetType.GetProperty(sourceProp.Name);
        if (targetProp != null && targetProp.CanWrite)
        {
            var value = sourceProp.GetValue(source);
            targetProp.SetValue(target, value);
        }
    }
    
    return target;
}

var personDto = new PersonDTO { Name = "Ivan", Age = 30 };
var person = Map<PersonDTO, Person>(personDto);

Пример 3: Dependency Injection контейнер

public class SimpleContainer
{
    private Dictionary<Type, Type> _registrations = new();
    
    public void Register<TInterface, TImplementation>()
        where TImplementation : TInterface
    {
        _registrations[typeof(TInterface)] = typeof(TImplementation);
    }
    
    public T Resolve<T>()
    {
        var interfaceType = typeof(T);
        var implementationType = _registrations[interfaceType];
        
        // Получаем конструктор
        var constructor = implementationType.GetConstructors().First();
        
        // Рекурсивно резолвим зависимости
        var parameters = constructor.GetParameters();
        var parameterInstances = parameters.Select(p => Resolve(p.ParameterType)).ToArray();
        
        // Создаём объект
        return (T)Activator.CreateInstance(implementationType, parameterInstances);
    }
    
    private object Resolve(Type type)
    {
        // Похожая логика для не-generic типов
        ...
    }
}

Производительность рефлексии

Рефлексия медленнее обычного кода в 100-1000 раз. Вот сравнение:

var person = new Person { Name = "Ivan" };
var type = person.GetType();
var property = type.GetProperty("Name");

// Обычное чтение: ~0.01 микросекунд
var name1 = person.Name;

// Рефлексия: ~1-10 микросекунд
var name2 = property.GetValue(person);

Оптимизация рефлексии

1. Кешируй Type и PropertyInfo

// ❌ Медленно — ищет тип каждый раз
foreach (var person in people)
{
    var type = person.GetType();
    var prop = type.GetProperty("Name");
    var name = prop.GetValue(person);
}

// ✅ Быстро — кешируем
var type = typeof(Person);
var property = type.GetProperty("Name");
foreach (var person in people)
{
    var name = property.GetValue(person);
}

2. Используй Expression Trees для частого использования

public class FastPropertyGetter<T>
{
    private Func<T, object> _getter;
    
    public FastPropertyGetter(string propertyName)
    {
        var property = typeof(T).GetProperty(propertyName);
        var parameter = Expression.Parameter(typeof(T));
        var memberAccess = Expression.Property(parameter, property);
        var cast = Expression.Convert(memberAccess, typeof(object));
        var lambda = Expression.Lambda<Func<T, object>>(cast, parameter);
        _getter = lambda.Compile();  // Компилируем в IL
    }
    
    public object GetValue(T instance) => _getter(instance);  // Близко к нормальной скорости
}

3. Используй Source Generators (C# 9+)

Инстеад of рефлексии, let compiler generate code:

[GenerateMapper]
public partial class PersonMapper { }
// Compiler generates fast mapping code automatically

Когда использовать рефлексию

Используй рефлексию для:

  • Сериализации/десериализации
  • Фреймворков (DI, ORM, MVC)
  • Плагинов и расширяемости
  • Валидации и маппинга
  • Когда тип известен только во время выполнения

НЕ используй в:

  • Горячих loop'ах с высокой частотой
  • Обработке микросекундных данных
  • Когда есть альтернатива (generics, delegate'ы)

Выводы для интервью

  • Рефлексия позволяет изучать и манипулировать типами во время выполнения
  • Это мощный инструмент для фреймворков, но медленнее обычного кода в 100+ раз
  • Всегда кеши Type и PropertyInfo если используешь рефлексию в loop'ах
  • Source Generators — современная альтернатива рефлексии
  • Используй только когда действительно нужна динамичность