CodeGym /Kurslar /C# SELF /Dinamiki tiplər ( dynamic) və DLR

Dinamiki tiplər ( dynamic) və DLR

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

1. Giriş

C# — güclü tipli dillərin nümayəndəsidir. Bu o deməkdir ki, kompilyasiya mərhələsində dəyişənin müəyyən tipi olmalıdır və sinif üzvlərinə yalnız həmin tipə uyğun şəkildə müraciət etmək olar. Bu təhlükəsizdir, rahatdır, IDE və kompilyatora aydındır.

Amma bəzən obyektlərin strukturu kompilyasiya vaxtında məlum olmaya bilər — məsələn, belə hallarda:

  • .NET-də başqa dildə yazılmış kod icra olunur (məsələn IronPython və ya IronRuby), burada tip sistemləri dinamikdir.
  • COM-obyektləri ilə işləyərkən (məsələn, Microsoft Office ilə Interop vasitəsilə).
  • Dinamik məlumat strukturları ilə qarşılıqlı əlaqədə: məsələn, zəif formalı JSON-obyektləri.
  • Çoxlu boilerplate yazmaq istəmirsən və obyektə “söz verib” onun metodunu adla çağıraq.

C# 4.0-da dynamic açar sözü və DLR-in gəlməsi ilə “Python kimi, amma C#-da” davranan kod yazmaq mümkün oldu: yoxlama runtime-da olacaq, kompilyasiya vaxtında yox. Bu sehrli söz kompilyatora daha yumşaq olmağı əmr edir və lazım olan metod/sahənin olub-olmadığını yalnız proqram həmin yerə gəldikdə yoxlamağa imkan verir.

DLR nədir?

Dynamic Language Runtime (DLR) — CLR (Common Language Runtime) üçün infrastruktur əlavəsidir, dinamik dillərin (və statik dillərdə dinamik davranışın) dəstəyini reallaşdırmağa imkan verir. DLR kompilyatorun əvvəlcədən yoxlaya bilməyəcəyi əməliyyatları icra etməyi üzərinə götürür: məsələn, tip yalnız proqram işləyərkən məlum olduqda metoda adla zəng etmək. DLR sayəsində C# “dinamik rejim” qazana bilir.

2. dynamic açar sözü: necə işləyir

object-dən fərqi

Bir çox yeni başlayanlar düşünür ki, dynamic sadəcə object-in daha mod bir formasıdır və hər dəfə cast etmək lazımdır. Belə deyil.

  • object tipli dəyişən metodları çağırmaq üçün həmişə konkret tipə açıq cast tələb edir (və kompilyator bunu yoxlayır).
  • dynamic tipli dəyişən kompilyatora deyir: “mən bilirəm nə var — runtime-da həll edəcəyik!”. Əgər metod yoxdursa — runtime-da istisna atılacaq.

Nümunə:


// Nümunə object ilə
object obj = "Privet, C#!";
// obj.ToUpper(); // Kompilyasiya səhvi! Kompilyator obj-nin string olduğunu bilmir
string s = ((string)obj).ToUpper(); // Yalnız bu şəkildə

// Nümunə dynamic ilə
dynamic dyn = "Privet, C#!";
string upper = dyn.ToUpper(); // Kompilyator etiraz etmir, yoxlama runtime-da olacaq

dynamic vasitəsilə mövcud olmayan sahələrə və metodlara müraciət etmək mümkündür. Əgər onlar yoxdursa — RuntimeBinderException tipli istisna olacaq.

Tətbiq nümunəsində istifadə

Tutaq ki, bizim mini-tətbiqdə JSON məlumatları ilə işləmə bloku var və struktur uçuş zamanı dəyişə bilər. dynamic ilə sahələrə elə bil həmişə varmış kimi müraciət etmək olar:


dynamic user = new System.Dynamic.ExpandoObject();
user.Name = "Vasya";
user.Age = 35;
Console.WriteLine($"Imya: {user.Name}, vozrast: {user.Age}");

3. DLR işləyərkən: qapağın altında nə baş verir

Dəyişən dynamic kimi elan olunduqda, C# kompilyatoru yadda saxlayır ki, həmin obyekt üzərində metodlara, sahələrə, indexerlərə, operatorlara və s. hər hansı müraciət əvvəlcədən yoxlana bilməz.

Bunun əvəzinə, hər hansı dinamik çağırış yerinə sistemin “dynamic dispatcher”ına sorğu yerləşdirilir. DLR yoxlayır: biz müraciət etdiyimiz metod/sahə həqiqətən mövcuddurmu? Əgər varsa — çağırış icra olunur, yoxdursa — istisna atılır.

Vizuallaşdırma sxemi


+------------------------+
| Kod s dynamic          |
+----------+-------------+
           |
           v
+----------+-------------+
| C# kompilyatoru əlavə |
| "sorğu DLR-ə"         |
+----------+-------------+
           |
           v
+----------+-------------+
| Runtime-da DLR axtarır|
| metod/sahə            |
+----------+-------------+
           |
      /---------\
      |         |
   tapıldı   tapılmadı
      |         |
      v         v
  çağırış   istisna
   metodu    (RuntimeBinderException)

DLR obyekt daxilində metod və sahələri axtarmağa cavabdehdir — və tez-tez istifadə olunan dinamik çağırışları cache-ə salır!

Bir az tarixçə

DLR dinamik olaraq yaranmış dillərin (məsələn IronPython, IronRuby) dəstəyi üçün yaradılmışdı və C#-da dinamik obyektlərlə inteqrasiyanı asanlaşdırmaq üçün nəzərdə tutulub. Bu gün ExpandoObject, DynamicObject, həmçinin COM və dinamik seriyalaşdırılmış məlumatlarla işləyəndə geniş istifadə olunur.

4. Müqayisə: static vs dynamic

Gəlin statik və dinamik paradigma üzrə sahə və metod çağırışlarının necə göründüyünə baxaq:

Yanaşma Elan Kompilyasiya zamanı yoxlama Səhv zamanı davranış
object
object obj = ...
Müvafiq tipə cast tələb edir Kompilyasiya səhvi və ya InvalidCastException
dynamic
dynamic dyn = ...
Xeyr, hamısı runtime-da RuntimeBinderException, əgər metod yoxdursa
statik tip
MyClass a = ...
Kompilyator tərəfindən yoxlanılır Kompilyasiya səhvi

Nümunə üçün vizual:


// Statik tipizasiya
MyUser u = new MyUser();
u.PrintInfo(); // Kompilyator yoxlayır

// dynamic
dynamic du = new MyUser();
du.PrintInfo(); // Kompilyator yoxlamır, metod yalnız runtime-da tapılacaq

// object
object ou = new MyUser();
// ou.PrintInfo(); // Kompilyasiya səhvi
((MyUser)ou).PrintInfo(); // Tipə açıq cast etmək lazımdır

Statik metodlar və dynamic


dynamic d = "Hello";
Console.WriteLine(d.Length); // 5 — hər şey yaxşıdır

Console.WriteLine(d.FakeMethod()); // RuntimeBinderException runtime-da

Amma ehtiyatlı olmaq lazımdır! Əgər ad səhv yazılıbsa və ya metod mövcud deyilsə — bu yalnız tətbiq işləyərkən üzə çıxacaq. Klassik “production-da səhv” nümunəsi.

5. Praktiki nümunələr: real tapşırıqlarda dinamika

ExpandoObject ilə iş


using System.Dynamic;

dynamic person = new ExpandoObject();
person.Name = "Sergey";
person.Greet = (Action)(() => Console.WriteLine($"Privet, {person.Name}!"));

person.Greet(); // Çap edəcək: Privet, Sergey!

Burada biz uçuş zamanı Name sahəsi və hətta Greet delegatını əlavə etdik. JavaScript obyektləri ilə işləməyə çox bənzəyir.

JSON strukturlarına bağlanma


using System.Dynamic;
using Newtonsoft.Json;

string json = @"{""name"": ""Andrey"", ""age"": 30}";
dynamic obj = JsonConvert.DeserializeObject
  
   (json); Console.WriteLine(obj.name); // Andrey Console.WriteLine(obj.age); // 30 
  

Diqqət: əgər obj.surname yazılsa — bu runtime zamanı səhv yaradacaq.

COM-Interop (Office Automation)


dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
excel.Visible = true; // Sahəni "gördük", baxmayaraq ki, kompilyator onun tipini bilmir

6. Faydalı nüanslar

DLR, dynamic və digər .NET dilləri ilə istifadə

  • IronPython/IronRuby: bu dillərin obyektlərini instansiya edib onların metodlarını C#-dan dynamic vasitəsilə çağıra bilərsiniz.
  • COM-obyektlər: metod və sahələrin avtomatik qaydada qablaşdırılması dynamic vasitəsilə.
  • Custom dinamik obyektlər: IDynamicMetaObjectProvider interfeysini implementasiya etməklə öz sinfinizi dinamik çağırışlarla təmin edə bilərsiniz.

IronPython ilə inteqrasiya nümunəsi


using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

ScriptEngine engine = Python.CreateEngine();
dynamic py = engine.Execute("class Test: pass\nTest()");
py.x = 42;
Console.WriteLine(py.x); // 42

Öz dinamik tiplər necə reallaşdırılır

Adətən daha asandır ki, DynamicObject-dan törənəsiniz.


using System.Dynamic;

class MyDynamic : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (binder.Name == "Ping")
        {
            result = "Pong!";
            return true;
        }
        result = null;
        return false;
    }
}

dynamic d = new MyDynamic();
Console.WriteLine(d.Ping); // Pong!
Console.WriteLine(d.Miss); // null (istisna atmır)

Harada dynamic ziyan verir

  • Performansı aşağı salır (dinamik çağırışlar statik olanlardan yavaşdır).
  • Debug etməyi və dəstəyi çətinləşdirir.
  • IDE-nin çoxlu faydalı imkanlarını alır: autocompletion, refactoring, usage search itir.
  • İstifadəçidə və ya serverdə aşkarlanan səhvlərə səbəb ola bilər.

Real olaraq dynamic istifadə etməli olduğunuz hallar

  • Xarici kitablxanalarla inteqrasiya, burada wrapper yazmaq mümkün və ya məqsədəuyğun deyil (COM, Python, JavaScript).
  • Zəif tipli strukturlar (JSON/XML/ExpandoObject) ilə manipulyasiya.
  • Framework, metaproqramlaşdırma və dinamik olması nəzərdə tutulmuş çevik API-lər yazarkən.

Digər bütün hallarda statik tiplərdən istifadə etmək və kompilyatorun köməyindən zövq almaq daha yaxşıdır :-)

7. dynamic və DLR ilə işləyərkən tipik səhvlər

Səhv №1: Mövcud olmayan üzvlərə müraciət.
dynamic üçün mövcud olmayan metod və ya sahəyə zəng runtime-da RuntimeBinderException-a gətirib çıxarır, bu da inkişaf mərhələsində tutmaq çətindir.

Səhv №2: Tip yoxlamasını gözardı etmək.
Çağırışdan əvvəl tip yoxlaması etmədən (məsələn, if (obj is IDictionary<string, object>)) gözlənilməz runtime səhvləri ala bilərsiniz.

Səhv №3: Public API-larda dynamic istifadə etmək.
Dinamik tiplər kodun saxlanmasını çətinləşdirir, çünki IDE və analizatorlar düzgün üzvləri göstərə bilmirlər, bu da oxunurluğu azaldır.

Səhv №4: Performans kritik kodda dynamic-dən sui-istifadə.
Dinamik çağırışlar statik olanlardan yavaşdır və overhead artırır. dynamic-i yalnız lazım olduqda istifadə edin.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
`dynamic`-in sadə istifadəsi
`dynamic`-in sadə istifadəsi
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION