نماینده‌ها (برنامه‌نویسی)

از ویکی‌پدیا، دانشنامهٔ آزاد
پرش به: ناوبری، جستجو

نماینده‌ها در سی شارپ در یک نگاه می‌توان گفت نماینده‌ها (Delegates) نوع داده‌ای در دات نت هستند که برای نگهداری آدرس متدها استفاده می‌شوند.

اینچنین متصور بشید که یک متغیر از یک نوع داده‌ای ِ نماینده (delegate) تعریف می‌کنید، سپس تعدادی متد (هم امضاء با نماینده) را بهش اضافه می‌کنید. سپس با invoke کردن، همه متدها اجرا می‌شوند. دقت شود که متدها سریال و نه موازی اجرا می‌شوند. معمولاً به ترتیب اضافه کردن.

امضای متد[ویرایش]

هر متد دارای یک نوع داده‌ای برگشتی (void راهم یک نوع داده‌ای در نظر بگیرید) و صفر یا چند آرگومان ورودی/خروجی می‌باشد. این دو ویژگی امضای یک متد نامیده می‌شود. توجه شود که تعداد آرگومان‌ها، نوع داده‌ای آرگومان‌ها، ورودی یا خروجی بودن آرگومان و جابه جایی موقعیت آرگومان‌ها امضای متد را تغییر می‌دهند. در سی شارپ ما قادر به نوشتن متدهای بی نام و نوع‌های داده‌ای نامشخص (ANONYMOUS Types) هستیم. سی شارپ در این زمینه کاملاً قوی عمل می‌کند.

تعریف نماینده و ثبت متدها در متغیر نماینده[ویرایش]

شیوه تعریف یک نماینده به صورت زیر است:

public delegate void MyDelegate(int n);
  • public: میزان دسترسی به نوع
  • delegate: کلمه کلیدی برای تعریف نماینده
  • void: نوع برگشتی متدهای پذیرنده (بسته به امضای متد)
  • (int n): آرگومان‌های متدهای پذیرنده (بسته به امضای متد)

از تعریف بالا متوجه می‌شویم که این نماینده متدهایی که امضای آنها شامل: نوع برگشتی void و یک آرگومان ورودی از نوع int باشد را می‌پذیرد.

نماینده‌ها یک نوع داده‌ای مستقل هستند یعنی می‌توان آنها را مثل کلاس‌ها و شمارنده مستقیماً در یک فضای نام (namespace) ایجاد کرد و لزومی ندارد که حتماً داخل یک کلاس تعریف شوند.

تعریف نماینده در یک فرم ویندوزی

namespace Rme_Delegates
{
    public delegate void MyDelegate(int n);// تعریف نماینده
 
    public partial class Form1: Form
    {
      private MyDelegate _delVar;// تعریف متغیر از روی نماینده
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void MethodXWYZ(int no)
        {
            this.listBox1.Items.Add("MethodXWYZ: "+no);
        }
 
        protected virtual void MethodEXPse(int pwr)
        {
            this.listBox1.Items.Add("MethodEXPse: " + (pwr * pwr));
        }
    };
}

متغیر خصوصی _delVar در شروع کار مقدار null دارد. متد تعریف شده را با علامت + به _delVar اضافه می‌کنیم:

_delVar += new MyDelegate(this.MethodXWYZ);

به نحوه اضافه کردن متد به متغیر نماینده توجه کنین!!! فقط نام متد به تنهایی لازم است. کلمه this اختیاری است.

و خلاصه تر: روش اول گویا تر است

_delVar += this.MethodXWYZ;
_delVar += this.MethodEXPse;

روش حذف متد نیز استفاده از - (منها) است بدین صورت:

_delVar -= this.MethodXWYZ;
_delVar -= this.MethodEXPse;

در ضمن: اگه نیاز به ثبت فقط یک متد در متغیرنماینده دارید از = (مساوی، انتساب) استفاده کنید:

_delVar = this.MethodEXPse;

فراخوانی (invoke) متدهای یک متغیر نماینده[ویرایش]

قبل از فراخوانی هر متغیر نماینده باید از تهی نبودن آن اطمینان حاصل کنید. این مسأله در برنامه‌های چند نخی خیلی بیشتر مورد نیاز است. فراخوانی یک متغیر نماینده‌ای همانند فراخوانی متد می‌باشد با این تفاوت که این بار پرانتز را جلوی یک متغیر باز می‌کنیم نه یک متد! مثال زیر نحوه فراخوانی را نمایش می‌دهد:

if (_delVar != null) _delVar(25);

بدین طریق متهای ثبت شده در متغیر یکی پس از دیگری با مقدار (اینجا ۲۵) اجرا می‌شوند.


متدهای بی نام[ویرایش]

نحوه اضافه کردن (ثبت کردن) متد به نماینده ها در (۲ تعریف نماینده و ثبت متدها در متغیر نماینده) توضیح داد شد. اما در برنامه نویسی های حرفه ای همیشه روال کار بدین صورت نیست. تعریف زیاد متدهای کوچک وقت گیر و باعث پیچیده تر شدن کد و در نهایت سردرگمی میشود. متدهای بی نام ترفندی است که کامپایلر سی شارپ و نه صرفاً CRL جهت رفع این مشکل به کار می برد. این مورد در LINQ بیشتر فواید خود را نشان می دهد. در کل زمانی که به متدهای خطی نیاز باشد و آدرس و نام و نشان متد مهم نباشد از متدهای بی نام استفاده می شود. متدهای بی نام در دو دسته قرار می گیرند:

  • متدهای بی نام با استفاده از کلمه delegate
  • عبارت لاندا Lambda Expression

متدهای زیر را در نظر بگیرید:

private static void methodOne()
{
    Console.WriteLine("Empty method");
}
private static void methodTwo(int number)
{
    if (number >= 10)
        Console.WriteLine("Yes");
    else Console.WriteLine("No");
}
private static void methodThree(int number, bool state)
{
    if (number >= 10)
        Console.WriteLine(state ? "Yes" : "No");
    else Console.WriteLine(state ? "True" : "False");
 
}

برای هر سه متد نماینده متناسب را تعریف می کنیم:

internal delegate void DelegateOne();
internal delegate void DelegateTwo(int n);
internal delegate void DelegateThree(int n, bool b);

نحوه تعریف متغیرهای نماینده ای و ثبت متدها و اجرای آن ها بدین صورت است:

DelegateOne d1 = Program.methodOne;
DelegateTwo d2 = Program.methodTwo;
DelegateThree d3 = Program.methodThree;
 
d1();
d2(10);
d3(10, true);
 
Console.ReadKey();//pause screen

تا به حال روش استاندارد که در بخش قبل توضیح داده شده بود را مرور کردیم. همین کار را با متدهای بی نام و با کلمه کلیدی delegateانجام می دهیم:

تعریف و استفاده از متدهای بی نام

DelegateOne d1 = delegate { Console.WriteLine("Empty method"); };
DelegateTwo d2 = delegate(int number)
{
    if (number >= 10)
        Console.WriteLine("Yes");
    else Console.WriteLine("No");
};
DelegateThree d3 = delegate(int number, bool state)
    {
        if (number >= 10)
            Console.WriteLine(state ? "Yes" : "No");
        else Console.WriteLine(state ? "True" : "False");
    };
 
d1();
d2(10);
d3(10, true);
Console.ReadKey();//pause screen

در اینجا نیازی به تعریف متدها درون کلاس نیست و بصورت خطی در جلوی نمایندهء مربطه تعریف می شوند. دقت شود که نوشتن کلمه delegate اجباری است همچنین برای متدهای دارای آرگومان نوشتن نوع آرگومان ها اجباری است.

عبارت لاندا Lambda Experssion[ویرایش]

با توجه به تعریف ها مثال (متدهای بی نام) عبارت لاندا را در مثال اینچنین بیان می کنیم:

DelegateOne d1 = () => Console.WriteLine("Empty method");
DelegateTwo d2 = (number) =>
{
    if (number >= 10)
        Console.WriteLine("Yes");
    else Console.WriteLine("No");
};
 
DelegateThree
d3 = (number, state) => // first
    {
        if (number >= 10)
            Console.WriteLine(state ? "Yes" : "No");
        else Console.WriteLine(state ? "True" : "False");
    };
d3 += (int number, bool state) =>//second
    {
        if (number >= 10)
            Console.WriteLine(state ? "Yes" : "No");
        else Console.WriteLine(state ? "True" : "False");
    };
d1();
d2(10);
d3(10, false);
 
Console.ReadKey();//pause screen

در تعریف عبارت لاندا از کلیدواژه => استفاده میشود. نوشتن نوع آرگومان ها اختیاری است.

کاربرد نماینده ها و رخدادها (events)[ویرایش]

نماینده ها در تعریف رخدادها کاربردی ذاتی دارند. همانگونه که شی گرایی بدون رخدادها بی معنی میشود. یک رخداد در طول زمان احتمالاً رخ خواهد داد. برای نمایش عکس العمل مناسب در هین رخ داد متد/هایی باید ثبت نام کرده باشند. متدهای ثبت نام شده در متغیر نماینده ای متناسب با رخداد ذخیره میشوند. کد زیر تعریف نماینده و رخداد پایان (Terminate Event) را شرح میدهد.

internal delegate void DelegateFormEvent();
 
private static DelegateFormEvent _delOfEvent;
public static event DelegateFormEvent Terminate
{
    add { _delOfEvent += value; }
    remove { _delOfEvent -= value; }
}

متدهای لازم را اینچنین در رخداد ثبت می کنیم:

Terminate += () => Console.WriteLine("Handled with me");

در اینجا از عبارت لاندا برای ایجاد و ثبت متد در رخداد استفاده کردیم. همانگونه که مشاهده می شود نحوه ثبت/حذف متد در/از یک رخداد دقیقاً مانند ثبت/حذف متد در/از یک متغیر نماینده ای می باشد. با این تفاوت که میتوان ثبت متد در رخداد را با بلوک add کنترل کرد و کد مورد نیاز را تهیه کرد. بلوک remove نیز هنگام حذف متد از رخداد اجرا میشود. بدین ترتیب می توان مدیریت کاملی بر متدهای یک رخداد داشت. مواظب کد نویسی در این قسمت باشید!!!

یک رخداد در زمان مناسب باید برانگیخته شود. برای برانگیختن رخداد اینچنین کد مینویسیم:

if (_delOfEvent != null) _delOfEvent();//raise terminate event

برانگیختن یک رخداد برابر است با فراخوانی (اجرا یا invoke ) کردن نماینده مربوطه است.

کاربرد نماینده ها در Generic[ویرایش]

کاربرد نماینده ها در LINQ[ویرایش]

پیوند به بیرون[ویرایش]