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 |
|---|---|
|
.NET-də tipin təsvirini göstərir (məsələn, class, interface, enum və s.) |
|
Tipin property-si |
|
Tipin metodu |
|
Tipin field-ı |
|
Tipin konstruktoru |
|
Assembly-ni (EXE və ya DLL) təmsil edir |
Type sinifinin əsas metodları
| Metod | Təsvir |
|---|---|
|
Bütün public property-ləri qaytarır |
|
Bütün public field-ları qaytarır |
|
Bütün public metodları qaytarır |
|
Bütün public konstruktorları qaytarır |
|
Tipin implementasiya etdiyi bütün interface-ləri qaytarır |
|
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.
GO TO FULL VERSION