واگذاری (برنامه‌نویسی شیءگرا)
واگذاری یا تفویض در برنامه‌نویسی شیءگرا، به «ارزیابی» یک عضو (ویژگی یا شگرد) از یک شیء (گیرنده) در زمینه شیء اصلی دیگر (فرستنده) گفته می‌شود. واگذاری را می‌توان به صورت صریح انجام داد، در این حالت شیء فرستنده به شیء گیرنده ارسال می‌شود، این موضوع را می‌توان در هر زبان شیءگرا انجام داد؛ یا اینکه واگذاری را به صورت ضمنی انجام داد، این کار از طریق «قواعد جستجوی عضو» در آن زبان انجام می‌شود، که نیاز به پشتیبانی‌هایی توسط آن زبان در آینده دارد. واگذاری ضمنی، شگرد اساسی برای استفاده مجدد از رفتار، در برنامه‌نویسی مبتنی بر پیش‌الگو است، و این موضوع مشابه با وراثت در برنامه‌نویسی مبتنی بر کلاس است. معروف‌ترین زبان‌هایی که از واگذاری در سطح زبان پشتیبانی می‌کنند، در درجه اول، زبان سلف است که از مفهوم واگذاری از طریق مفهوم « ی والد » استفاده می‌کند، این اسلات‌ها، بر اساس جستجوی شگرد در «فراخوانی‌های خودی» استفاده می‌شوند، و سپس زبان جاوا اسکریپت است ( را ببینید).
نماینده‌ها در سی شارپ در یک نگاه می‌توان گفت نماینده‌ها (Delegates) نوع داده‌ای در دات نت هستند که برای نگهداری آدرس متدها استفاده می‌شوند.
اینچنین متصور بشید که یک متغیر از یک نوع داده‌ایِ نماینده (delegate) تعریف می‌کنید، سپس تعدادی متد (هم امضاء با نماینده) را بهش اضافه می‌کنید. سپس با invoke کردن، همه متدها اجرا می‌شوند. " دقت شود که متدها سریال و نه موازی اجرا می‌شوند. معمولاً به ترتیب اضافه کردن. "
هر متد دارای یک نوع داده‌ای برگشتی (void راهم یک نوع داده‌ای در نظر بگیرید) و صفر یا چند آرگومان ورودی/خروجی می‌باشد. این دو ویژگی امضای یک متد نامیده می‌شود. توجه شود که تعداد آرگومان‌ها، نوع داده‌ای آرگومان‌ها، ورودی یا خروجی بودن آرگومان و جابه جایی موقعیت آرگومان‌ها امضای متد را تغییر می‌دهند. در سی شارپ ما قادر به نوشتن متدهای بی‌نام و نوع‌های داده‌ای نامشخص (ANONYMOUS Types) هستیم. سی شارپ در این زمینه کاملاً قوی عمل می‌کند.
شیوه تعریف یک نماینده به صورت زیر است:
public delegate void MyDelegate(int n);
از تعریف بالا متوجه می‌شویم که این نماینده متدهایی که امضای آن‌ها شامل: نوع برگشتی void و یک آرگومان ورودی از نوع int باشد را می‌پذیرد.
نماینده‌ها یک نوع داده‌ای مستقل هستند یعنی می‌توان آن‌ها را مثل کلاس‌ها و شمارنده مستقیماً در یک فضای نام (namespace) ایجاد کرد و لزومی ندارد که حتماً داخل یک کلاس تعریف شوند.
تعریف نماینده در یک فرم ویندوزی
namespace Rme_Delegates
متغیر خصوصی _delVar در شروع کار مقدار null دارد. متد تعریف شده را با علامت + به _delVar اضافه می‌کنیم: _delVar += new MyDelegate(this.MethodXWYZ);
به نحوه اضافه کردن متد به متغیر نماینده توجه کنین!!! فقط نام متد به تنهایی لازم است. کلمه this اختیاری است.
و خلاصه تر: روش اول گویا تر است
_delVar += this.MethodXWYZ; _delVar += this.MethodEXPse;
روش حذف متد نیز استفاده از - (منها) است بدین صورت:
_delVar -= this.MethodXWYZ; _delVar -= this.MethodEXPse;
در ضمن: اگه نیاز به ثبت فقط یک متد در متغیرنماینده دارید از = (مساوی، انتساب) استفاده کنید:
_delVar = this.MethodEXPse; قبل از فراخوانی هر متغیر نماینده باید از تهی نبودن آن اطمینان حاصل کنید. این مسئله در برنامه‌های چند نخی خیلی بیشتر مورد نیاز است. فراخوانی یک متغیر نماینده‌ای همانند فراخوانی متد می‌باشد با این تفاوت که این بار پرانتز را جلوی یک متغیر باز می‌کنیم نه یک متد! مثال زیر نحوه فراخوانی را نمایش می‌دهد: if (_delVar != null) _delVar(25);
بدین طریق متدهای ثبت شده در متغیر یکی پس از دیگری با مقدار (اینجا ۲۵) اجرا می‌شوند.
نحوه اضافه کردن (ثبت کردن) متد به نماینده‌ها در (۲ تعریف نماینده و ثبت متدها در متغیر نماینده) توضیح داد شد. اما در برنامه‌نویسی‌های حرفه‌ای همیشه روال کار بدین صورت نیست. تعریف زیاد متدهای کوچک وقت گیر و باعث پیچیده‌تر شدن کد و در نهایت سردرگمی می‌شود. متدهای بی نام ترفندی است که کامپایلر سی شارپ و نه صرفاً CRL جهت رفع این مشکل به کار می‌برد. این مورد در LINQ بیشتر فواید خود را نشان می‌دهد. در کل زمانی که به متدهای خطی نیاز باشد و آدرس و نام و نشان متد مهم نباشد از متدهای بی نام استفاده می‌شود. متدهای بی نام در دو دسته قرار می‌گیرند: متدهای زیر را در نظر بگیرید:
private static void methodOne() private static void methodTwo(int number) private static void methodThree(int number, bool state)
برای هر سه متد نماینده متناسب را تعریف می‌کنیم:
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) DelegateThree d3 = delegate(int number, bool state)
d1(); d2(10); d3(10, true); Console.ReadKey();//pause screen
در اینجا نیازی به تعریف متدها درون کلاس نیست و به صورت خطی در جلوی نمایندهٔ مربطه تعریف می‌شوند. دقت شود که نوشتن کلمه delegate اجباری است همچنین برای متدهای دارای آرگومان نوشتن نوع آرگومان‌ها اجباری است.
با توجه به تعریف‌ها مثال (متدهای بی‌نام) عبارت لاندا را در مثال اینچنین بیان می‌کنیم:
DelegateOne d1 = () => Console.WriteLine("Empty method"); DelegateTwo d2 = (number) =>
DelegateThree d3 = (number, state) =>// first d3 += (int number, bool state) =>//second d1(); d2(10); d3(10, false);
Console.ReadKey();//pause screen
در تعریف عبارت لاندا از کلیدواژه => استفاده می‌شود. نوشتن نوع آرگومان‌ها اختیاری است.
نماینده‌ها در تعریف رخدادها کاربردی ذاتی دارند. همانگونه که شی گرایی بدون رخدادها بی‌معنی می‌شود. یک رخداد در طول زمان احتمالاً رخ خواهد داد. برای نمایش عکس‌العمل مناسب در هین رخ داد متد/هایی باید ثبت نام کرده باشند. متدهای ثبت نام شده در متغیر نماینده‌ای متناسب با رخداد ذخیره می‌شوند. کد زیر تعریف نماینده و رخداد پایان (Terminate Event) را شرح می‌دهد.
internal delegate void DelegateFormEvent();
private static DelegateFormEvent _delOfEvent; public static event DelegateFormEvent Terminate
متدهای لازم را اینچنین در رخداد ثبت می‌کنیم:
Terminate += () => Console.WriteLine("Handled with me");
در اینجا از عبارت لاندا برای ایجاد و ثبت متد در رخداد استفاده کردیم. همانگونه که مشاهده می‌شود نحوه ثبت/حذف متد در/از یک رخداد دقیقاً مانند ثبت/حذف متد در/از یک متغیر نماینده‌ای می‌باشد. با این تفاوت که می‌توان ثبت متد در رخداد را با بلوک add کنترل کرد و کد مورد نیاز را تهیه کرد. بلوک remove نیز هنگام حذف متد از رخداد اجرا می‌شود. بدین ترتیب می‌توان مدیریت کاملی بر متدهای یک رخداد داشت. مواظب کدنویسی در این قسمت باشید!!!
یک رخداد در زمان مناسب باید برانگیخته شود. برای برانگیختن رخداد اینچنین کد می‌نویسیم:
if (_delOfEvent != null) _delOfEvent();//raise terminate event
برانگیختن یک رخداد برابر است با فراخوانی (اجرا یا invoke) کردن نماینده مربوطه است.