مقدمه
زبان C# در هستهی خود یک زبان با نوعدهی ایستا (Statically Typed)
است. این یعنی نوع تمام متغیرها، پارامترها و مقادیر بازگشتی باید در زمان کامپایل مشخص باشد و
کامپایلر این انواع را به شدت بررسی میکند. این ویژگی باعث افزایش امنیت، کارایی و قابلیت نگهداری
کد میشود. با این حال، دنیای نرمافزار همیشه ایستا نیست. ما اغلب نیاز داریم تا با سیستمهایی که
ماهیت پویا (dynamic) دارند، مانند زبانهای اسکریپتنویسی (مثل پایتون و جاوااسکریپت) یا فرمتهای
دادهای مانند JSON، تعامل داشته باشیم.
در درسهای قبل دیدیم که چگونه میتوان با استفاده از انعکاس (Reflection) و اتصال دیرهنگام (Late
Binding)، به صورت پویا با انواع کار کرد. اما این روش میتواند طولانی و پیچیده باشد. برای
سادهسازی این سناریوها، C# 4.0 کلمهی کلیدی dynamic و یک
زیرساخت جدید به نام Dynamic Language Runtime (DLR) را معرفی کرد. این
ویژگیها به C# اجازه میدهند تا در کنار ماهیت ایستای خود، از قابلیتهای برنامهنویسی
دینامیک نیز بهرهمند شود.
کلمهی کلیدی dynamic
کلمهی کلیدی dynamic به کامپایلر میگوید: "من به تو اطمینان میدهم
که عملیاتی که روی این متغیر انجام میدهم، در زمان اجرا معتبر خواهد بود. لطفاً هیچگونه بررسی
نوعی را در زمان کامپایل برای آن انجام نده و این کار را به زمان اجرا موکول کن."
وقتی یک متغیر را از نوع dynamic تعریف میکنید، شما میتوانید هر متد یا پراپرتی را بر روی آن
فراخوانی کنید، گویی که آن عضو حتماً وجود دارد. کامپایلر این کد را بدون خطا میپذیرد. سپس در زمان
اجرا، DLR وارد عمل شده و تلاش میکند تا آن عضو را بر روی شیء واقعی پیدا و اجرا کند.
اگر عضو مورد نظر وجود داشته باشد، کد با موفقیت اجرا میشود. اگر وجود نداشته باشد، یک استثناء در
زمان اجرا (runtime exception) پرتاب خواهد شد.
Program.cs
dynamic d = "Hello, World!";
Console.WriteLine(d.Length);
try
{
d.NonExistentMethod();
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
{
Console.WriteLine(ex.Message);
}
d = 100;
Console.WriteLine(d + 50);
این مثال به وضوح قدرت و در عین حال خطر dynamic را نشان میدهد. ما از تمام مزایای بررسی زمان
کامپایل صرفنظر کردهایم و مسئولیت صحت فراخوانیها را به زمان اجرا منتقل کردهایم.
مقایسهی dynamic و object
در نگاه اول، ممکن است dynamic شبیه به object به نظر برسد، زیرا هر دو میتوانند هر نوع
دادهای را در خود نگه دارند. اما یک تفاوت بسیار بنیادی بین آنها وجود دارد:
- System.Object: این نوع، ایستا (static) است. کامپایلر میداند که نوع متغیر
object است. برای دسترسی به اعضای خاص نوع واقعی که در آن ذخیره شده، شما
باید آن را به صورت صریح به آن نوع تبدیل (cast) کنید. اگر تبدیل نامعتبر
باشد، کامپایلر (در صورت امکان) یا CLR (در زمان اجرا) خطا میدهد.
- dynamic: این نوع، پویا (dynamic) است. کامپایلر وانمود میکند که نوع متغیر
را نمیداند. شما میتوانید هر عضوی را مستقیماً بر روی آن فراخوانی کنید بدون نیاز به
تبدیل نوع. بررسی صحت این فراخوانی به طور کامل به زمان اجرا موکول میشود.
Program.cs
object obj = "A string in an object";
dynamic dyn = "A string in a dynamic";
int length1 = ((string)obj).Length;
int length2 = dyn.Length;
Console.WriteLine($"Length via object: {length1}");
Console.WriteLine($"Length via dynamic: {length2}");
کاربردهای اصلی برنامهنویسی دینامیک
با توجه به از دست رفتن امنیت نوع، باید از dynamic با احتیاط و فقط در سناریوهایی که واقعاً به
آن نیاز است، استفاده کنیم.
۱. تعامل با زبانهای پویا
این اصلیترین دلیل ایجاد dynamic بود. اگر شما از داخل C# با کدی که در یک زبان پویا
مانند پایتون (با استفاده از IronPython) یا روبی (با IronRuby) نوشته شده تعامل دارید، dynamic
این کار را بسیار ساده میکند. شما میتوانید به سادگی اشیاء آن زبان را در یک متغیر dynamic قرار
داده و متدهای آنها را فراخوانی کنید.
۲. سادهسازی کد انعکاس (Reflection)
در درس قبل دیدیم که فراخوانی دینامیک یک متد با استفاده از انعکاس میتواند طولانی و پیچیده باشد.
dynamic میتواند این کد را به شدت ساده کند.
Program.cs
public class MyClass { public void DoSomething(string message) => Console.WriteLine(message); }
Type myType = typeof(MyClass);
object instance1 = Activator.CreateInstance(myType);
MethodInfo method = myType.GetMethod("DoSomething");
method.Invoke(instance1, new object[] { "Hello via Reflection" });
dynamic instance2 = Activator.CreateInstance(myType);
instance2.DoSomething("Hello via dynamic");
۳. کار با دادههای JSON
یکی از کاربردهای بسیار رایج، کار با دادههای JSON است. کتابخانههایی مانند
Newtonsoft.Json به شما اجازه میدهند تا یک رشتهی JSON را به یک شیء dynamic تبدیل
کنید و به راحتی به پراپرتیهای تودرتوی آن دسترسی پیدا کنید، بدون اینکه نیاز به تعریف کلاسهای
متناظر داشته باشید.
Program.cs
using Newtonsoft.Json;
string json = @"{ 'Name': 'John Doe', 'Address': { 'City': 'New York' } }";
dynamic data = JsonConvert.DeserializeObject(json);
Console.WriteLine($"Name: {data.Name}, City: {data.Address.City}");