امروز قصد دارم یکی از مفاهیم مفید زبانهای پویا با نام Dynamic Dispatch یا اعزام پویا را برای شما دوستان تشریح کنم و همچنین روشهای مختلف پیاده سازی آنرا در C# برای شما تشریح خواهم نمود.
ابهامات بسیاری درباره کلماتی شبیه dispatch، binding و typing وجود دارد، بنابراین فکر می کنم بهتر است ابتدا ببینیم مفهوم Dispatc یا اعزام چیست. به عبارت ساده، اعزام روشی است که زبان برنامه نویسی یک متد یا تابع را فراخوانی میکند و در کل مهم نیست که یک متد به یک نمونه (instance) یا یک کلاس تعلق داشته باشد.
دو نوع اعزام وجود دارد. یکی به صورت ایستا یا Static و دیگری به صورت پویا یا Dynamic. مفهوم اولی این است که هر متد در زمان کامپایل شناخته می شود. متدهای اعزامی به صورت پویا در زمان اجرا و بر اساس نوع پارامترهای آن تعیین می شوند.
همچنین، زبانهای اعزامی پویا را می توان به دو گروه تقسیم نمود: اعزام تکی (single) یا چندتایی (multiple). در اعزام تکی فقط یک پارامتر برای انتخاب یک متد ارسال میشود. به عبارت دیگر، در اعزام چندتایی این مزیت وجود دارد که شما میتوانید تعداد بیشتری پارامتر برای انتخاب یک تابع یا متد ارسال کند.
زبانهای بسیاری هستند که از اعزام تکی پشتیبانی میکنند و سی شارپ نیز یکی از آنهاست تا این که نسخه 4.0 آن رسید. معمولاً این نوع از اعزام با استفاده از مدل وراثت و متدهای مجازی (virtual) پیاده سازی میشوند. شما از یک کلاس پدر ارث بری میکنید، یک متد را override می کنید و در زمان اجرا و بر اساس نوع شی جاری، متد را فراخوانی میکنید.
اما آیا روش دیگری هم وجود دارد که با استفاده از آنها بتوان از مزایای اعزام پویا در سی شارپ استفاده کرد؟
فرض کنیم که چند کلاس در یک شی وراثتی ساده داریم که به هر دلیل نمیتوان کدی را به آنها اضافه کرد (مثلاً کدها برای خودمان نیست).
public interface IBar {}
public class Bar : IBar {}
public sealed class FooBar : Bar {}// Simple helper for demonstration
public static class ConsolePrinter
{
public static void Print(IBar item)
{
Console.WriteLine("IBar");
}
public static void Print(Bar item)
{
Console.WriteLine("Bar");
}
public static void Print(FooBar item)
{
Console.WriteLine("FooBar");
}
}
var bar = new Bar();
var foo = new FooBar();
IBar ibar = new FooBar();ConsolePrinter.Print(bar);
ConsolePrinter.Print(foo);
ConsolePrinter.Print(ibar);// prints Bar
// prints FooBar
// prints IBar
IBar[] items = { bar, foo, ibar };
foreach (var item in items)
{
ConsolePrinter.Print(item);
}// prints IBar
// prints IBar
// prints IBar
foreach (dynamic item in items)
{
ConsolePrinter.Print(item);
}// prints Bar
// prints FooBar
// prints FooBar
var actions = new Dictionary<Type, Action<IBar>>
{
{typeof (IBar), ConsolePrinter.Print},
{typeof (Bar), i => ConsolePrinter.Print((Bar)i)},
{typeof (FooBar), i => ConsolePrinter.Print((FooBar)i)}
};foreach (var item in items)
{
actions[item.GetType()](item);
}
public interface IBar
{
void Accept(IBarVisitor visitor);
}public class Bar : IBar
{
public virtual void Accept(IBarVisitor visitor)
{
visitor.Visit(this);
}
}public sealed class FooBar : Bar
{
public override void Accept(IBarVisitor visitor)
{
visitor.Visit(this);
}
}public interface IBarVisitor
{
void Visit(FooBar item);
void Visit(Bar item);
}public sealed class PrintBarVisitor : IBarVisitor
{
public void Visit(FooBar item)
{
ConsolePrinter.Print(item);
}public void Visit(Bar item)
{
ConsolePrinter.Print(item);
}
}
var visitor = new PrintBarVisitor();foreach (var item in items)
{
item.Accept(visitor);
}
برچسب:
نویسنده: محمد رضا جوادیان