CodeGym /Kurslar /C# SELF /Refleksiya: System.Reflect...

Refleksiya: System.Reflection

C# SELF
Səviyyə , Dərs
Mövcuddur

1. Giriş

Refleksiya (ing. Reflection) — proqramınızın icra zamanı öz strukturunu araşdırmaq imkanıdır. Təsəvvür edin ki, proqramınız öz-özünə soruşa bilər: 'Məndə hansı metodlar var? Bu tipin hansı property-ləri var?' və runtime-da cavab ala bilər.

Refleksiyanın real həyatda tətbiq nümunələri

  • Plugin və modulların yaradılması: Hər hansı bir siniflə işləyəcəyinizi əvvəlcədən bilməyə ehtiyac yoxdur — kifayətdir DLL-i yükləyəsiniz və onun metodlarını runtime-da öyrənəsiniz.
  • Test avtomatlaşdırması: Test framework-ləri, məsələn xUnit və NUnit, test metodlarını refleksiya ilə tapır.
  • Serialization: Refleksiya ilə istənilən obyektin JSON/XML-ə avtomatik çevrilməsini təmin etmək olar, property-ləri əl ilə gəzintiyə ehtiyac qalmadan.
  • Atributlara əsasən data validasiyası: Məsələn, bir property-yə [Required] atributu əlavə etmisinizsə — bütün belə property-ləri avtomatik yoxlamaq istəyə bilərsiniz.

Müsahibələrdə niyə bilmək lazımdır

Refleksiya bacarığı tez-tez müsahibələrdə soruşulur, çünki bu .NET-in daxili iş prinsiplərini başa düşdüyünüzü və dinamik tipli problemləri həll edə bildiyinizi göstərir. Öz framework-lərinizi və ya extension-larınızı yazmaq istəyirsinizsə, refleksiya vazgeçilmazdır.

2. Refleksiya namespace-i və əsas siniflər

C#-da refleksiya funksionallığının hamısı System.Reflection namespace-i vasitəsilə əldə edilir. Faylın əvvəlində unutmayın:

using System.Reflection;

Refleksiyanın əsas tipləri

Sinif/Tip Təsvir
Type
.NET-də tipin təsvirini göstərir (məsələn, class, interface, enum və s.)
PropertyInfo
Tipin property-si
MethodInfo
Tipin metodu
FieldInfo
Tipin field-ı
ConstructorInfo
Tipin konstruktoru
Assembly
Assembly-ni (EXE və ya DLL) təmsil edir

Type sinifinin əsas metodları

Metod Təsvir
GetProperties()
Bütün public property-ləri qaytarır
GetFields()
Bütün public field-ları qaytarır
GetMethods()
Bütün public metodları qaytarır
GetConstructors()
Bütün public konstruktorları qaytarır
GetInterfaces()
Tipin implementasiya etdiyi bütün interface-ləri qaytarır
GetCustomAttributes()
Tipə tətbiq olunmuş bütün atributları qaytarır

Bu metodlara tez-tez private və ya static üzvlərə giriş üçün BindingFlags ötürülür, amma hələlik əsas ssenari ilə kifayətlənəcəyik.

3. İlk addım: Type obyektini almaq

Refleksiyanın bütün yanaşmaları Type obyekti ilə işləməyə əsaslanır.

Bu obyekti almağın bir neçə yolu var, gəlin hər birinə baxaq:

Yol 1: Obyektdən

string text = "Hello, Reflection!";
Type type1 = text.GetType();
Console.WriteLine(type1.Name); // String

Yol 2: Tip adından

Type type2 = typeof(int);
Console.WriteLine(type2.FullName); // System.Int32

Yol 3: Sətirdən (dinamik)

Type type3 = Type.GetType("System.Double");
Console.WriteLine(type3); // System.Double

Diqqətli olun! İstifadəçi sinifləri başqa assembly-dən olduqda tipin tam adını və assembly-ni göstərmək lazım gəlir.

4. Tipin strukturunu öyrənmək: property-lər, metodlar, field-lar və konstruktorlar

İndi həqiqət vaxtıdır: Type obyektini aldıq — və indi istədiyimiz hər şeyi öyrənə bilərik.

Property siyahısını almaq

Type type = typeof(DateTime);
PropertyInfo[] properties = type.GetProperties();

foreach (var prop in properties)
{
    Console.WriteLine($"{prop.PropertyType.Name} {prop.Name}");
}

Çıxış:

Int32 Day
Int32 Month
Int32 Year
DayOfWeek DayOfWeek
...

Metod siyahısını almaq

MethodInfo[] methods = type.GetMethods();

foreach (var method in methods)
{
    Console.WriteLine($"{method.ReturnType.Name} {method.Name}()");
}

Böyük tiplərdə metodlar çox ola bilər! Çox vaxt sizə hamısı lazım deyil, yalnız "öz" metodlar (məsələn, object-dən miras alınanları istisna etməklə) lazım olur. Buna sonra toxunacağıq.

Field siyahısını almaq

FieldInfo[] fields = type.GetFields();

foreach (var field in fields)
{
    Console.WriteLine($"{field.FieldType.Name} {field.Name}");
}

Amma adi siniflərin çoxunun public field-ı olmur — kapsulyasiya bizim hamımızın dostudur :)

Konstruktorları almaq

ConstructorInfo[] constructors = type.GetConstructors();

foreach (var ctor in constructors)
{
    Console.WriteLine($"Konstruktor: {ctor}");
}

5. Praktikada refleksiyanı tətbiq edək: tətbiqimizə baxış

Xatırladım ki, tədris layihəmizdə vəzifələri saxlayan TaskItem sinifi var. Gəlin onu refleksiya ilə araşdıraq.

public class TaskItem
{
    public int Id { get; set; }
    public string Title { get; set; }
    public DateTime DueDate { get; set; }
    public bool IsCompleted;
}

Beləliklə, tipimizin bütün "içliyini" runtime-da belə öyrənə bilərsiniz:

Type taskType = typeof(TaskItem);

Console.WriteLine("Property-lər:");
foreach (var prop in taskType.GetProperties())
    Console.WriteLine($"  {prop.PropertyType.Name} {prop.Name}");

Console.WriteLine("Field-lar:");
foreach (var field in taskType.GetFields())
    Console.WriteLine($"  {field.FieldType.Name} {field.Name}");

Console.WriteLine("Metodlar:");
foreach (var method in taskType.GetMethods())
    Console.WriteLine($"  {method.ReturnType.Name} {method.Name}()");

Sinifə yeni property-lər əlavə edib refleksiyanın onları necə gördüyünü yoxlayın. Belə işləyirlər universal serializer-lər və obyekt inspektorları.

6. Vizualizasiya: Type obyekti necə qurulub (sxem)


+-------------------------+
|         Type            |
+-------------------------+
|  .Name                  |
|  .FullName              |
|  .Namespace             |
|  .Assembly              |
+-------------------------+
|  .GetProperties()       |
|  .GetFields()           |
|  .GetMethods()          |
|  .GetConstructors()     |
|  .GetInterfaces()       |
|  .GetCustomAttributes() |
+-------------------------+

Bu, tipin "portreti"dır — adı, assembly-si, üzvlər dəsti və atributları var.

Tip barədə əsas məlumatın alınması

Type obyekti tipin nə olduğunu deyə bilər:

Type t = typeof(double);

Console.WriteLine(t.Name);         // Double
Console.WriteLine(t.FullName);     // System.Double
Console.WriteLine(t.Namespace);    // System
Console.WriteLine(t.IsClass);      // False
Console.WriteLine(t.IsValueType);  // True
Console.WriteLine(t.IsEnum);       // False
Console.WriteLine(t.IsPrimitive);  // True

7. Ad üzrə konkret property, metod və ya field axtarmaq

Property

var prop = taskType.GetProperty("Title");
if (prop != null)
{
    Console.WriteLine($"'Title' property-sinin tipi: {prop.PropertyType}");
}

Metod

var method = taskType.GetMethod("ToString");
if (method != null)
{
    Console.WriteLine($"'ToString' metodu qaytarır: {method.ReturnType}");
}

Field

var field = taskType.GetField("IsCompleted");
if (field != null)
{
    Console.WriteLine($"'IsCompleted' field-ının tipi: {field.FieldType}");
}

8. Dəyərlərə dinamik giriş üçün refleksiyadan istifadə

İndi yalnız baxmırıq, həm də dəyərlərə toxunuruq!

Property dəyərinin oxunması

TaskItem item = new TaskItem { Id = 1, Title = "Refleksiyanı test et", DueDate = DateTime.Today };
PropertyInfo titleProp = item.GetType().GetProperty("Title");

if (titleProp != null)
{
    object value = titleProp.GetValue(item);
    Console.WriteLine($"Başlıq: {value}");
}

Property dəyərinin təyin edilməsi

titleProp.SetValue(item, "Dəyişdirilmiş başlıq");
Console.WriteLine(item.Title);

Field-larla iş

FieldInfo completedField = item.GetType().GetField("IsCompleted");
completedField.SetValue(item, true);
Console.WriteLine(item.IsCompleted); // true

Private field və property-lər üçün xüsusi flag-lər lazımdır, məsələn BindingFlags.NonPublic. Buna sonra baxacağıq.

9. Metodların refleksiya ilə çağırılması

Metodları yazarkən bilmədən belə çağırmaq olar!

TaskItem task = new TaskItem { Title = "Refleksiya — əla!" };
MethodInfo method = task.GetType().GetMethod("ToString");

if (method != null)
{
    object result = method.Invoke(task, null);
    Console.WriteLine(result);
}

Əgər metod parametrə malikdirsə, onları object massiv kimi ötürmək lazımdır:

public class Calculator
{
    public int Add(int a, int b) => a + b;
}

var calc = new Calculator();
var addMethod = typeof(Calculator).GetMethod("Add");
object[] parameters = { 5, 3 };
object sumResult = addMethod.Invoke(calc, parameters);
Console.WriteLine(sumResult); // 8

10. Konstruktor vasitəsilə obyektin dinamik yaradılması

Refleksiya ilə new operatoru olmadan da obyekt yaratmaq mümkündür!

Type taskType = typeof(TaskItem);
object newTask = Activator.CreateInstance(taskType);
// Bu eyni TaskItem-dır (amma tip — object)
Console.WriteLine(newTask.GetType().Name); // TaskItem

Konstruktor parametrləri də ötürmək olar:

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

Type userType = typeof(User);
object user = Activator.CreateInstance(userType, "Elis");
Console.WriteLine(((User)user).Name); // Elis

11. System.Reflection ilə işləyərkən tipik səhvlər

Səhv №1: Private üzvlərə BindingFlags olmadan müraciət.
Default olaraq refleksiya yalnız public üzvləri görür. BindingFlags.NonPublic olmadan private field və ya metodlara giriş mümkün deyil.

Səhv №2: null yoxlamasını laqeyd etmək.
GetProperty, GetMethod və digər metodlar üzv tapılmadıqda null qaytara bilər. Yoxlama olmadan bu NullReferenceException-a gətirər.

Səhv №3: Metod overload-unu düzgün seçməmək.
Bir neçə overload olduqda GetMethod düzgün olmayan versiyanı qaytara bilər. Parametrlərin tiplərini göstərərək uyğun overload-u seçin.

Səhv №4: Performans kritik kodda refleksiyadan istifadə etmək.
Refleksiya birbaşa çağırışlardan daha yavaşdır. Yüksək performanslı kod üçün metadata-nı cache etmək və ya delegate-lərdən istifadə edin.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION