JavaRush /Курси /C# SELF /Використання рефлексії

Використання рефлексії

C# SELF
Рівень 63 , Лекція 1
Відкрита

1. Вступ

У C# будь-який клас (або структура) — це набір членів. До них належать:

  • Поля (fields): змінні, що зберігають дані.
  • Властивості (properties): «розумні» обгортки з get/set.
  • Методи (methods): функціональність.
  • Конструктори (constructors): способи створити екземпляр.
  • Події (events): публікація-підписка по-розумному.

Уявіть, що у вас є магічна лупа, з якою ви можете зазирнути всередину будь-якого типу, дізнатися його вміст і навіть змінити його. Це й є рефлексія.

Простір імен і базові типи рефлексії

Перш ніж почати, не забудьте імпортувати простір імен:

using System.Reflection;

Ключові класи та інтерфейси:

Клас/інтерфейс Опис
Type
Вихідна точка — усе починається з нього
FieldInfo
Інформація про поле
PropertyInfo
Інформація про властивість
MethodInfo
Інформація про метод
ConstructorInfo
Інформація про конструктор
EventInfo
Інформація про подію
MemberInfo
Спільний «батько» для всіх перелічених

2. Отримуємо члени типу через рефлексію

Отримуємо поля та властивості

Приклад: перелічення полів і властивостей

Припустімо, маємо такий клас:

public class Person
{
    public string Name { get; set; }
    public int Age;
    private string Secret = "Таємне слово";
}

Отримуємо поля:

Type personType = typeof(Person);

FieldInfo[] fields = personType.GetFields(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var field in fields)
{
    Console.WriteLine($"Поле: {field.Name}, Тип: {field.FieldType.Name}, Доступ: {field.Attributes}");
}

Отримуємо властивості:

PropertyInfo[] properties = personType.GetProperties(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var property in properties)
{
    Console.WriteLine($"Властивість: {property.Name}, Тип: {property.PropertyType.Name}");
}

Пояснення

  • BindingFlags — це набір «прапорців», які вказують, які члени шукати (public/private, instance/static тощо).
  • Поля й властивості різняться: поле — шматочок даних, властивість — це методи get/set, що приховують поле.

Отримуємо методи

MethodInfo[] methods = personType.GetMethods(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var method in methods)
{
    Console.WriteLine($"Метод: {method.Name}");
}

Ви здивуєтеся, скільки методів буде виведено. Там будуть і гетери/сетери властивостей, і службові, успадковані від object!

Корисний фільтр: щоб отримати лише власні «явні» методи, можна додати фільтрацію за імʼям або атрибутом.

Що шукати з якими BindingFlags

Що шукаємо Приклад виклику Опис
Публічні поля
GetFields(BindingFlags.Public | ...)
Усе, що видно іншим
Приватні поля
GetFields(BindingFlags.NonPublic | ...)
Приховані внутрішні
Лише екземпляр
BindingFlags.Instance
Лише нестатичні
Лише статичні
BindingFlags.Static
Лише статичні
Усе одразу
BindingFlags.Public | NonPublic | Instance | Static
Повний список

3. Динамічне створення екземпляра типу

Чому це круто?
Можна створити об’єкт, не знаючи його точного типу на етапі компіляції.

Простий спосіб — через Activator.CreateInstance

// Отримуємо Type будь-яким способом, наприклад:
Type myType = typeof(Person);
// Створюємо об’єкт:
object obj = Activator.CreateInstance(myType);
// Тепер obj — це екземпляр Person, але як object

Якщо клас не має конструктора без параметрів, можна передати параметри:

public class Animal
{
    public string Name { get; }
    public Animal(string name) { Name = name; }
}

// ...
Type animalType = typeof(Animal);
object dog = Activator.CreateInstance(animalType, "Шарик");

Важливо!

Console.WriteLine(dog.GetType().Name); // Animal

Гнучкіший спосіб — через ConstructorInfo

ConstructorInfo ctor = animalType.GetConstructor(new Type[] { typeof(string) });
object dog = ctor.Invoke(new object[] { "Шарик" });

У реальному житті зазвичай використовують Activator, але іноді потрібні точні параметри або робота з приватними конструкторами — саме тут і стане у пригоді ConstructorInfo.

4. Доступ до полів і властивостей за імʼям

Отримуємо й змінюємо поле

Person p = new Person();
Type t = p.GetType();

FieldInfo field = t.GetField("Age");
field.SetValue(p, 42);
Console.WriteLine(field.GetValue(p)); // 42

Робота з приватними полями:

FieldInfo secret = t.GetField("Secret", BindingFlags.NonPublic | BindingFlags.Instance);
if (secret != null)
{
    secret.SetValue(p, "Ой, таємницю розкрито");
    Console.WriteLine(secret.GetValue(p));
}
else
{
    Console.WriteLine("Поле Secret не знайдено");
}

Увага! Не зловживайте доступом до приватних полів — це злам інкапсуляції та потенційні помилки.

Отримуємо й змінюємо властивості

PropertyInfo pi = t.GetProperty("Name");
pi.SetValue(p, "Василь");
Console.WriteLine(pi.GetValue(p)); // Василь

Властивості — це не поля, а методи get/set за кадром.

Чому інколи не працює?

Якщо ваше поле або властивість приватні, обов’язково додавайте прапорці BindingFlags з потрібними значеннями (наприклад, NonPublic). Якщо у властивості немає сетера, ви не зможете нічого туди записати через рефлексію — правило зберігається.

5. Виклик методів за імʼям

Виклик простого методу без параметрів

public class Greeter
{
    public void SayHello()
    {
        Console.WriteLine("Привіт!");
    }
}

Greeter g = new Greeter();
Type t = g.GetType();
MethodInfo mi = t.GetMethod("SayHello");
mi.Invoke(g, null); // Привіт!

Виклик методу з параметрами

public class MathOp
{
    public int Add(int x, int y) => x + y;
}

MathOp calc = new MathOp();
Type t = calc.GetType();
MethodInfo mi = t.GetMethod("Add");
object result = mi.Invoke(calc, new object[] { 7, 5 });
Console.WriteLine(result); // 12

Якщо метод статичний, у перший аргумент Invoke передайте null.

Робота з перевантаженими методами

MethodInfo mi = t.GetMethod(
    "Add", 
    new Type[] { typeof(int), typeof(int) }
);

Або перебирайте й фільтруйте вручну:

foreach(var method in t.GetMethods()) {
    if (method.Name == "Add" && method.GetParameters().Length == 2) 
    {
        // ... наш метод!
    }
}

6. Корисні нюанси

Основні члени, методи й властивості для аналізу типу

Член класу Метод для отримання інформації Клас результату Як читати/змінювати значення
Поле (field)
GetField, GetFields
FieldInfo
.GetValue(obj), .SetValue(obj, x)
Властивість (prop)
GetProperty, GetProperties
PropertyInfo
.GetValue(obj), .SetValue(obj, x)
Метод (method)
GetMethod, GetMethods
MethodInfo
.Invoke(obj, параметри)
Конструктор
GetConstructor, GetConstructors
ConstructorInfo
.Invoke(параметри)

Навіщо все це в реальному житті?

  • В ORM‑фреймворках (наприклад, Entity Framework будує SQL-запити за вашими моделями).
  • У системах серіалізації та парсерах (наприклад, JSON-серіалізатори).
  • У універсальних бібліотеках для логування, тестування і побудови UI.
  • На співбесідах — охоче питають: «як би ви написали універсальний копіювальник об’єкта?».
  • Для написання плагінів, коли ваша програма завантажує й викликає код, написаний іншими.

Навіть якщо вам зараз здається, що це якась магія з фільмів про хакерів, повірте — стане у пригоді!

7. Типові помилки й особливості

Дуже часто розробники плутаються з BindingFlags — не бачать приватні поля або, навпаки, отримують забагато методів. Завжди перевіряйте поєднання прапорців: для приватних полів використовуйте NonPublic, для статичних — Static тощо.

Під час виклику методів через Invoke передавайте аргументи в тому самому порядку й типі, що й в оголошенні. Неправильний порядок або тип призведе до TargetParameterCountException або ArgumentException. Повернене значення має тип object — не забудьте виконати перетворення типу (або скористатися pattern matching).

Не забувайте про продуктивність: рефлексія повільніша за прямі виклики. У гарячих місцях кешуйте знайдені Type/MethodInfo/PropertyInfo або заздалегідь генеруйте делегати.

Ви часто побачите службові методи на кшталт get_PropertyName і set_PropertyName. Це методи доступу до властивостей — їх вручну створювати не треба.

Якщо змінюєте приватні поля чи властивості — ви ламаєте інкапсуляцію. Неправильне використання може призвести до важковловимих помилок. Як казав дядько Бен: «З великою силою приходить велика відповідальність» — використовуйте обережно, особливо в бібліотечних проєктах.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ