1. مهمان گرامی، جهت ارسال پست، دانلود و سایر امکانات ویژه کاربران عضو، ثبت نام کنید.
    بستن اطلاعیه

آموزش جامع تصویری و کاربردی سی شارپ (#c)

شروع موضوع توسط minaaa ‏10/11/11 در انجمن C #C++

  1. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    Interface ها در سي شارپ
    در پست هاي قبلي به دو سطح از Inheritance اشاره كرديم. در سطح اول يك كلاس را از يك كلاس ديگر به صورت معمولي به ارث برديم. يعني مثلا كلاس Emp را از كلاس Person به ارث برديم در حالتيكه ساختن شيء‌از هر دو آن ها كاربري و منطقي بود.
    سپس يك سطح انتزاعي تر كلاس هاي abstract را بررسي كرديم. كلاس Emp را به صورت abstract تعريف كرديم و كلاس هاي MonthlyEmp و HourlyEmp را از آن به ارث برديم.

    حالا می خواهیم به بالاترین سطح انتزاعی در سی شارپ یعنی Interface ها بپردازیم. در بررسی اینترفیس ها من ابتدا به تعریف آن ها اشاره می کنم. سپس روش و Syntax استفاده از آن ها و در نهایت موارد استفاده از آن را بررسی می کنم.

    اينترفيس ها قرارداد هایی هستند که اعلام می کنند این نوع های داده ای دارای چه امکاناتی می باشند. اما روش پیاده سازی آن ها را اعلام نمی کنند. در واقع در اینترفیس ها شما هیچ گونه پیاده سازی ندارید و فقط اعلام می کنید که نوع شما دارای چه "متد ها" , "پراپرتی ها" , "ایندکس ها" و "رویدادهایی" است.

    برای تعریف اينترفيس ها در سي شارپ باید در سطح namespace همانند یک کلاس ولی با استفاده از keyword ی به نام interface.

    [​IMG]

    همانطور که در تصویر بالا مشاهده می کنین در اینترفيس ها شما فقط به تعریف ها می پردازید و اجازه ایجاد بدنه متد ها و property ها را ندارید. در تعریف interface ها قوانین زیر وجود دارند:

    • امکان استفاده از access modifier ها وجود ندارد. (در واقع تمامی اعضاء یک interface) به صورت public هستند ولی کلمه public نوشته نمی شود.
    • در اينترفيس ها Constructor نداريم.
    • تمامی Property و Method و Indexer ها به صورت abstract و بدون پیاده سازی هستند.
    • امکان استفاده از field ها وجود ندارد.
     
  2. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    در قسمت قبلی در رابطه با interface ها و اینکه چگونه ایجادشان کنیم صحبت کردیم. حالا اجازه بدین در رابطه با اینکه چرا و به چه دلایلی از interface ها استفاده می کنیم صحبت کنیم.


    موارد استفاده اينترفيس ها - Interface Usage

    همانطور که قبلا هم اشاره شد و از کلمه inteface بر می آید در واقع اینترفيس ها یک واسط یا قرارداد هستند. اما برای اینکه راحت تر دلایل استفاده از آن ها را در زبان برنامه نویسی سی شارپ و دنیای شي گرایی متوجه بشیم من سه دلیل برای استفاده از اینترفیس ها بیان می کنم:



    1. اینترفیس ها به عنوان استاندارد - Interface as Standard
    2. اینترفیس ها به عنوان سرویس - Interface as Service
    3. اینترفیس ها برای حل مشکل توارث چندگانه - Interface for Multiple Inheritance


    اینترفيس ها به عنوان استاندارد - Interface as Standard
    فرض بفرمائید که شما قصد تهیه یک Total System یا یک مجموعه نرم افزار یکپارچه را دارید. در این مجموعه نرم افزار Entity ها بسياری وجود دارند و در فاز تحلیل و طراحی نسبت به شناخت و طراحی آن ها اقدام کرده اید. وظيفه اجراي هر یک از این SubSystem ها توسط یک گروه از افراد در سازمان شما می باشد.

    حالا موجودیتی مثل افراد (Person) را در نظر بگیرید که در تمامی زیر سيستم های شما وجود دارد و فقط با جزئیات مختلف نمود پیدا می کند. مثلا در زیر سيستم حسابداری به عنوان مشتری با اطلاعات خاص مشتري ها , در سيستم پرسنلی به عنوان کارمند با اطلاعات خاص هر کارمند و ....

    نکته اینجاست که اگر قرار باشد این موجودیت ها بین زیر سیستم های این نرم افزار یکپارچه قابلیت تبادل داشته باشند باید یک استاندارد خاص در نظر گرفته شود که تمام این موجودیت های به نحوی به آن قابل تبدیل باشند. پس در این حالت یک inteface با تمامی اطلاعات مشترکي که موجودیت انسان در تمام این زیر سيستم ها دارد در نظر گرفته می شود و تمامی زير سيستم ها موظف به پیاده سازی آن توسط کلاس های خاص خود می شوند. و در صورتیکه لازم باشد یک موجود از این زير سيستم به زيرسيستم دیگر ارجاء شود به راحتی به IPerson تبدیل شده و در زير سيستم بعدی به عنوان يک IPerson دریافت و تبدیل می شود.


    [​IMG]

    البته این روش نه تنها در سي شارپ بلکه در جاهای دیگر نيز استفاده دارد. به عنوان مثال وقتی قرار به استفاده از تکنولوژی Bluetooth شد 5 شرکت پيشتاز این تکنولوژی یعنی Microsoft , Ericsson و سه شرکت دیگر برای استاندارد سازی این تکنولوژی سميناري تشکیل دادند و توافق نامه ای امضاء کردند که طبق آن تمامی شرکت ها موظف به توليد محصولاتي با رعايت یکسری استاندارد شدند و البته همه آن ها می توانستند برای توسعه اين تکنولوژی اقدام کنند. در نتيجه تمامي محصولاتي که اين شرايط را رعايت کنند مي توانند با يکديگر ارتباط داشته باشند. ​
     
  3. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    اینترفیس ها به عنوان سرویس - Interface as Service
    وقتی بستر دات نت را بررسی می کنید به تعداد زیادی interface برخورد می کنید که وقتی آن ها را پیاده سازی کنید , می توانید از یک سرویس استفاده کنید. در واقع به ارث بری از یک اینترفیس شما یک از سرویس برخوردار خواهید شد.

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

    [​IMG]

    همانطور که در تصویر بالا مشخص است شما می توانید با استفاده از متد Sort روی کلاس ArrayList که در واقع یک Collection می باشد محتویات آرایه را سورت نمائید و در نتیجه خروجی شبیه به تصویر زیر داشته باشید:

    [​IMG]

    حالا اگر در داخل یک ArrayList دیگر من چند شیء از جنس Person قرار دهم و باز متد Sort را فراخوانی کنم چه اتفاقی خواهد افتاد:

    [​IMG]

    همانطور که در تصویر زیر می بینید به جهت اینکه فریم ورک دات نت نمی داند که باید سورت را بر چه مبنایی روی تایپ های custom مثل Person انجام دهد یک Exception (خطا) پرتاب خواهد شد.

    [​IMG]

    در واقع در فریم ورک دات نت هرکجا شما به سورت کردن یک تایپ خاص مثل Person را نیاز داشتید کافیست که از یک Interface به نام IComparable به ارث برید. یا به بیانی دیگر مایکروسافت سرویس Sort را در غالب این اینترفيس ارائه می کند. حالا به کد زیر که پیاده سازی اینترفيس IComparable می باشد دقت کنید:

    [​IMG]

    همانطور که می بینید در داخل این اینترفیس یک متد به نام CompareTo وجود دارد که یک ورودی از جنس Object دارد و یک خروجی عددی. در صورتیکه عددی که از این متد خارج می شود بزرگتر از یک باشد به این معناست که شیء جاری(this) بزرگتر از پارامتر پاس شده می باشد و در صورتیکه عدد کوچکتر از صفر باشد به این معناست که شیء جاری کوچکتر از پارامتر پاس شده می باشد و اگر این دو باهم برابر باشد باید خروجی متد 0 باشد. در کدی که من نوشتم مبنای مقایسه را برروی سن افراد قرار دادم که در نتیجه این نوع پیاده سازی خروجی زیر از نمونه کد بالا حاصل خواهد شد:

    [​IMG]
     
  4. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    در قسمت قبلی در رابطه با اینکه اينترفيس ها را به عنوان سرويس در نظر بگيريم صحبت شد و گفتيم که به عنوان مثال در صورتيکه شما از اينترفيس IComparable به ارث بريد و متد CompareTo را پياده سازی کنيد آنگاه مي توانيد از سرويس Sort در کلاس ArrayList استفاده کنيد. اما چرا؟


    در واقع وقتي شما متد Sort را فراخواني مي کنيد در کلاس ArrayList فرض را بر این مي گذارد که تک تک اشياي داخل آرایه از این اينترفيس به ارث رفته اند در نتيجه شي داخل آرايه را (که يک object مي باشد) Cast به IComparable می کند در نتيجه مي تواند از متد CompareTo استفاده کرده و مقايسه مورد نظر را انجام دهد. حالا در صورتيکه شيء شما از اين اينترفيس به ارث نرفته باشد يک خطا از نوع InvalidOperationException دريافت خواهید کرد.

    [​IMG]

    نمونه های بسياري وجود دارند که شما با پياده سازي يک يا چند Interface امکان استفاده از يک موضوع (به صورت سرويس) را بهره مند مي شويد. البته به اين نکته توجه داشته باشيد که روش پياده سازي و Logic آن کاملا در اختيار شماست و در صورتيکه درست پياده سازي نشود مسئوليت خطا های احتمالی و يا عملکرد نادرست به عهده شما مي باشد.

    فرض کنيد که شما يک کلاس داريد که وظيفه آن چاپ کردن اطلاعات اشياء ديگر توسط يک چاپگر مي باشد. نکته مهم اين است که شما مي خواهيد اين سرويس (یعني چاپ کردن توسط يک چاپگر خاص) را در اختيار همه قرار دهيد. براي همين منظور کافيست که يک interface طراحي کنيد و يک متد به نام Print در آن تعريف کنيد.

    [​IMG]

    حالا کافيست که در اين کلاس از کاربران انتظار ارسال کلاس هايي را داشته باشيد که از اين اينترفيس به ارث رفته اند و در صورتيکه يک شيء به متد شما ارسال شود که از اين اينترفيس به ارث نرفته باشد شما هم يک خطا از نوع InvalidOperationException پرتاب خواهيد کرد.

    [​IMG]

    مثال هاي بسياري از اين دست در رابطه با استفاده از اينترفيس ها به عنوان سرويس مي توان نوشت. در پست بعدي در رابطه با استفاده از اينترفيس ها برای پياده سازي توارث چندگانه خواهم نوشت.
     
  5. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    اينترفيس براي توارث چندگانه - Interface for Multiple Inheritance
    در پاره اي از مواقع , به اين نتيجه مي رسيم که يک موجوديت در نرم افزار شما بايد از دو يا چند موجوديت ديگر به ارث برود. اما همانطور که قبلا هم اشاره کرده بودم در سي شارپ توارث چندگانه وجود ندارد پس شما نمي توانيد از چند کلاس همزمان به ارث برويد. راه حل اين سناريو ها استفاده از اينترفيس ها براي پياده سازي توارث چندگانه مي باشد. نکته اي که وجود دارد اين است که استفاده از اين روش باعث کم تر شدن کد نويسي شما نخواهد شد.

    فرض بفرمائيد که در طراحي يک سيستم براي يک شرکت توليد دو موجوديت "مشتري" و "کارمند" طراحي شده اند. هر مشتري داراي اطلاعاتي نظير نام و مبلغ اعتبار و یک متد برای خرید و يک متد براي نمايش اطلاعاتش می باشد. هر کارمند نيز داراي اطلاعاتي نظير نام و حقوق و یک متد برای نمايش اطلاعاتش می باشد. حالا شما به این نتيجه رسيده ايد که يکسري از کارمندان شرکت از شرکت خريد نيز انجام مي دهند يعني در واقع مشتري هم هستند. به همين دلیل تصميم گرفته ايد که يک کلاس به نام EmpCustomer ايجاد کنيد که هم از مشتري به ارث رفته باشد و هم از کارمند.

    خوب همانطور که گفتم اجراي اين سناريو با توجه به اينکه امکان به ارث رفتن از دو يا چند کلاس به طور همزمان وجود ندارد شما بايد از يک روش ديگر يعني استفاده از interface ها اقدام کنيد.

    پياده سازي توارث چندگانه با استفاده از اينترفيس ها

    همانطور که قبلا اشاره کرديم يک کلاس توانايي به ارث رفتن از يک کلاس و چندين اينترفيس را دارا مي باشد. براي همين منظور من دو اينترفيس به نام هاي ICustomer و IEmployee ايجاد مي کنم:

    [​IMG]

    سپس دو کلاس خود يعني Employee و Customer را از اينترفيس هاي متناظرشان به ارث مي برم و پياده سازي مي کنم:

    [​IMG]


    [​IMG]

    حالا کافيست کلاس سوم را از ايجاد و از هر دو اين اينترفيس ها به ارث مي بريم:

    [​IMG]

    همين طور که در تصوير مي بينيد در Print به جاي عبارت Employee یا Customer عبارت EmpCustomer چاپ خواهد شد.

    !! در اين روش قصد ما اصلا کمتر نوشتن کد نمي باشد بلکه فقط پياده سازي توارث چندگانه مي باشد.

    !! توجه داشته باشيد که وقتي يک کلاس از دو یا چند اينترفيس به ارث مي رود که داراي اطلاعات مشترکي هستند (مثل Name و Print در اين مثال) يکبار پياده سازي آن کافي است.

    حالا کلاس Company را ايجاد مي کنم و به جاي اينکه يک آرايه از جنس Customer براي مشتريان و يک آرايه از جنس Employee براي کارمندان در نظر بگيرم آرايه اي از ICustomer براي مشتريان و آرايه اي از IEmployee براي کارمندان در نظر خواهم گرفت.

    [​IMG]

    همانطور که در تصوير بالا مشاهده مي کنيد يک متد براي درج مشتريان به نام AddCustomer در نظر گرفته ام و نوع ورودي آن را از جنس ICustomer در نظر گرفته ام. همين روش براي متد AddEmployee هم با IEmployee انجام داده ام. در نتيجه شما مي توانيد اشيايي از جنس Employee و EmpCustomer را در ليست کارمندان و اشيايي از جنس Customer و EmpCustomer را در ليست مشتريان قرار دهيد​
     
  6. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    در مثال قبلي با پياده سازي کلاس EmpCustomer به مقصود خود رسيديم و در واقع مي توانستيم که اشيايي از اين نوع را هم به ICustomer و هم به IEmployee نسبت دهيم. حالا به کد زير دقت کنيد:

    [​IMG]


    همانطور که مي بينيد من 3 شيء جديد ايجاد کرده ام. 2 تا Employee و يک EmpCustomer و آنها را به عنوان کارمند در ليست کارمندان شرکت اضافه نموده ام. و همينطور 2 شيء ديگر که از نوع Customer هستند. و به همراه شيء قبلي که از جنس EmpCustomer بود به ليست مشتريان شرکت اضافه کرده ام. حالا اگر از شيء شرکت متد هاي چاپ ليست مشتريان و چاپ ليست کارمندان را فراخواني کنم نتيجه زير را خواهيم ديد.

    [​IMG]

    اين نتيجه در واقع نتيجه درستي است چرا که من 3 مشتري و 3 کارمند دارم. اما نکته اي که وجود دارد اين است که شيء EmpCustomer من در موقع نمايش اطلاعات خود ، با ما بقي اشياء من متفاوت است. يعني وقتي در ليست کارمندان نمايش داده مي شود تفاوت آن با بقيه کارمند و در ليست مشتريان با بقيه مشتريان مشهود است. اما من مي خواهم که در هر دو حالت کاملا شبيه به اين دو نوع باشد و رفتاري مشابه بقيه داشته باشد. در نتيجه من بايد از Explicit Interface Implementation استفاده کنم.

    Explicit Interface Implementation
    اين روش موقعي استفاده مي شود که شما مي خواهيد رفتار يک شيء را بسته به نوع reference آن تعيين کنيد. يعني وقتي به يک شيء از جنس EmpCustomer از دیدگاه ICustomer نگاه مي کنيد رفتاري شبیه مابقي ICustomer ها داشته باشد و وقتي از دید IEmployee نگاه مي کنيد رفتاري شبيه به مابقي IEmployee ها داشته باشد و در حالتي که از ديد EmpCustomer نگاه مي کنيد رفتار خاص ديگر داشت باشد. در مثال ما شما بايد متد Print را سه مرتبه پیاده سازي کنيد. به اين کد دقت کنيد:

    [​IMG]

    همانطور که مي بينيد در پياده سازي هاي دوم و سوم ابتدا نام interface و سپس دقيقا اسم متد را به همان ترتيب که در interface ها نوشته شده است و بدون access modifier مي نويسيم. در پياده سازي متد نيز کاملا شبيه به Employee و Customer عمل خواهيم کرد. در نتيجه اگر مثال قبلي را دوباره اجرا کنيد خروجي به شکل زير خواهيد داشت.
     
  7. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    Delegates in CSharp
    بعد از بررسی اينترفيس ها بايد به بررسي دليگيت ها بپردازيم. براي اين بررسي ابتدا من يک تعريف از Delegate خواهم گفت. سپس به روش ايجاد (Syntax) دليگيت ها مي بپردازم و در نهايت به دلايل استفاده یا مثال هاي آن خواهم پرداخت. لطفا سعي کنيد که در بررسي delegate ها کمي حوصله کنيد و با دقت مطالب را مطالعه کنيد.

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

    چگونه يک delegate تعريف کنيم؟
    من براي ايجاد يک delegate چهار مرحله در نظر مي گيرم.

    1. تعريف delegate یا Delegate Definition
    2. ايجاد reference از delegate یا Delegate Declaration
    3. ايجاد شيء یا Delegate Initialization
    4. فرخواني یا Calling
    اجازه بدين اين مراحل را با يک مثال ساده بررسي کنيم.
    مرحله اول بايد در namespace نوشته شود. همانند يک کلاس يا type هاي ديگر. وقتي مي خواهيم يک delegate را بنويسيم بايد بدانيم که اين delegate براي فراخواني چه متدهايي نوشته شده است.
    [​IMG]
    همان طور که در تصوير مي بينيد من يک delegate را در فضاي namespace تعريف کرده ام. با توجه به کد نوشته شده ، اين delegate امکان فرخواني متد هايي را که خروجي ندارند (void) و همينطور هيچ پارامتري هم ندارند ، دارد. استفاده از کلمه CallBack در انتهاي نام delegate ها پيشنهاد مي شود.

    حالا بايد يک reference از آن delegate ايجاد کنيم:

    [​IMG]
    در مرحله سوم بايد اين reference را new کنيم:

    [​IMG]

    در اين مرحله بايد شما نام يک متد و فقط نامش را به عنوان پارامتر به constructor اين delegate پاس دهيد. توجه کنيد که تمامي delegate ها داراي Constructorی با يک پارامتر مي باشند که اسم يک متد خواهد بود. متدي که پاس مي شود بايد دقيقا ساختاري شبيه به ساختار تعريف شده delegate شما (مرحله 1) داشته باشد.

    مرحله آخر فراخواني delegate است:

    [​IMG]

    وقتي يک شيء از يک delegate را با استفاده از () فراخواني مي کنيد در واقع متدي که داخل آن delegate تعريف شده است را فراخواني مي کنيد.

    نکته مهم اين است که شما مي توانيد بيش از يک متد (با ساختار شبيه به هم) را داخل يک delegate قرار دهيد. براي اين کار به جاي استفاده از = موقع new کردن از =+ استفاده خواهيم کرد. وقتي اين delegate را فراخواني مي کنيد تمامي آن ها به ترتيب فراخواني خواهند شد.
     
  8. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    در مثال قبلی در رابطه با چهار مرحله توليد و استفاده يک Delegate صحبت کرديم. دقت کنيد که معمولا مراحل توليد يک delegate کنار همديگر استفاده نمي شود. و اين مراحل بين چندين کلاس پخش مي شود تا استفاده اصلي آن مشخص شود.

    اجازه بدين با يک مثال ادامه بديم:

    يک بانک را در نظر بگيريد. مشريان اين بانک داراي اعتبار مشخصي مي باشند. در هنگام خريد اين اعتبار کمتر و کمتر خواهد شد. اين بانک داراي n بازرس است که در سطح شعب مختلف فعاليت مي کنند. بازرسان بانک وظيفه پيگيري وضعيت اين مشتري را دارند. پس وقتي خريد مشتري از اعتبارش بيشتر مي شود بايد اطلاعات مشتري به تمامي بارزسان اعلام شود تا پيگيري هاي لازم توسط نزديک ترين بازرس انجام شود.

    خوب پس من يک کلاس خواهم داشت به نام Customer و يک کلاس هم به نام Agent:

    [​IMG]

    همانطور که مي بينيد کلاس Customer داراي يک متد به نام Buy است که از اين طريق خريد انجام مي شود. نکته مهم اين است که بايد کدي بنويسيم که وقتي يک مشتري خريد مي کند تمام بازرسان متوجه خريد بيش از اعتبار وي شوند.

    [​IMG]

    در کلاس Agent يک ArrayList براي ثبت فعاليت هاي هر يک از بازرسان در نظر گرفته شده است که براي ثبت پيگيري جديد بايد از متد AddTask استفاده شود. پس ما بايد به طريقي AddTask تمامي بازرسان را همزمان و در متد Buy کلاس Customer فراخواني کنيم.

    براي انجام اين موضوع من يک Delegate متناسب با متد AddTask ايجاد مي کنم (مرحله اول).

    [​IMG]

    حالا در کلاس Agent يک متغيير static (براي همه بازرسان) از جنس آن delegate ايجاد مي کنم. (مرحله دوم)
    سپس در Constructor کلاس Agent متد AddTask هر يک از بازرسان را در delegate ثبت مي کنم. (مرحله سوم).
    [​IMG]
    در نهايت موقعي که اعتبار مشتري من منفي مي شود delegate static را فراخواني مي کنم. در نتيجه به تمامي بازرسان يک وظيفه جديد اضافه خواهد شد.

    [​IMG]
    و حالا کافيست چند شيء از هر کدام از کلاس ها بسازم و شروع به تست کنم:

    [​IMG]

    و در نتيجه:



    [​IMG]
    اين مثال را مرور کنيد. مسلما براي دوستاني که تازه با سي شارپ آشنا شده اند خيلي سنگين خواهد بود. اصلا مهم نيست کافيه که توي ذهنتون چند باري مرورش کنيد. به نظر من delegate سنگين ترين بحث سي شارپ است. پس اصلا به خودتون شک نکنيد!
     
  9. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    رويداد ها در سي شارپ - Events in CSharp
    اغلب نرم افزار هايي که توليد مي شوند ساختاري دارند. به عنوان مثال شما يک فرم ايجاد مي کنيد و کاربر با پر کردن اطلاعات فرم و در نهايت کليک بر روي گزينه ذخيره فرم اطلاعاتي مورد نظر را ذخيره مي نمايد.
    پر کردن فرم ، کليک بر روي گزينه Save و ... همگي رويداد هايي هستند که از طرف کاربر شما ارجاع مي شود و چک کردن اطلاعات و ذخيره کردن اطلاعات و ... هم پاسخ (عکس العمل) هاي شما به آن رويداد ها.

    براي توليد و استفاده يک رويداد در سي شارپ 7 مرحله پياده سازي وجود دارد. 5 مرحله اول براي توليد رويداد (Event Raise) و 2 مرحله آخر براي پاسخ به رويداد (Event Handler) مي باشد.

    در بررسي رويداد ها با يک مثال ساده شروع مي کنيم. يک انبار را در نظر بگيريد. در اين انبار وقتي تعداد يک کالا به صفر مي رسد يک رويداد بايد اعلام شود و در نتيجه آن رويداد مسئول انبار درخواست خريد چند آيتم از آن کالا را صادر خواهد کرد. کلاس انبار را به صورت زير تعريف ميکنم. براي ثبت محصول از متد AddProduct و براي دريافت کالا از متد GetProduct استفاده مي شود. در صورتيکه تعداد کالا به صفر برسد ، رويداد مورد نظر بايد اعلام شود.
    [​IMG]
    براي تعريف رويداد ، ابتدا يک delegate تعريف مي کنيم (مرحله اول):

    [​IMG]

    دقت کنيد که delegate هايي که به منظور توليد Event ها ايجاد مي شوند همواره داراي دو پارامتر مي باشند. پارامتر اول از نوع object که در واقع شيء است که رويداد بر روي آن اتفاق مي افتد. پارامتر دوم از نوع EventArgs یا کلاس هاي که از آن به ارث رفته باشد. پارامتر دوم در واقع اطلاعات يا آرگومان هاي رويداد مي باشد. 2
    در مرحله دوم يک event در کلاس Warehouse تعريف مي کنيم (مرحل دوم):

    [​IMG]

    در مرحله بعدي يک متد protected به نام OnLowAmount تعريف مي کنيم (مرحله چهارم3)

    [​IMG]

    و سپس در زمان مناسب (موقعي که تعداد کالا به صفر برسد) رويداد را با استفاده از متد protected مرحله قبل اعلام مي کنيم (مرحله پنجم):

    [​IMG]


    سپس شروع به استفاده از اين کلاس خواهم کرد:

    [​IMG]

    همانطور که مي بينيد در اين کلاس رويداد LowAmount به صورت يک Event (با شکلي شبيه علامت برق) مشخص شده است.

    مرحله بعدي ايجاد يک متد است که با ساختار delegate رويداد مورد نظر مطابقت داشته باشد (مرحله ششم):

    [​IMG]

    و در نهايـت وصل کردن اين متد (مرحله ششم) به رويداد با استفاده از =+ مي باشد. (مرحله هفتم):

    [​IMG]

    دقت فرمائيد که در صورتيکه تمايل داشته باشيد مي توانيد بيش از يک متد را داخل رويداد خود به عنوان EventHandler قرار دهيد:
    2. به عنوان مثال در رويداد KeyDown بر روي کلاس Form از کلاس KeyٍEventArgs استفاده شده است.

    3. در اين مثال نيازي به وجود مرحله سوم نمي باشد.


     
  10. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    رويداد ها و آرگيومنت هاي خاص - Event and Custom EventArgs

    بسیاري از رويداد ها در هنگام وقوع داراي جزئياتي مي باشند. به عنوان مثال رويداد MouseMouse داراي اطلاعات همچون محل Cursor موس مي باشد يا در رويداد KeyDown کليدي که تايپ شده است از اطلاعات خاص اين رويداد است. ايجاد رويداد ها و پاس کردن اطلاعات خاص آن رويداد ها توسط کلاس هايي که از کلاس پايه اي به نام EventArgs به ارث رفته اند ، اعلام مي گردد.

    براي بررسي اين موضوع از يک مثال استفاده مي کنيم. فرض کنيد که در مثال قبلي مي خواهيد که در موقع بروز رويداد LowAmount امکان جلوگيري از خريدي در حال وقوع را داشته باشيم. پس شما بايد يک متغيير boolean در پارامتر هاي رويدادتان به نام Cancel تعريف کنيد که در صورتي که توسط متد EventHandler به True ست شده باشد بايد از خريد جلوگيري نمائيد.

    براي اجراء اين موضوع يک کلاس به نام LowAmountEventArgs تعريف مي کنيم (اين کلاس از کلاس EventArgs به ارث مي رود) و در آن يک متغيير به نام Cancel از جنس bool تعريف مي کنم:

    [​IMG]

    سپس delegate مربوط به رويداد را به صورت زير تغيير مي دهم:

    [​IMG]

    همچنين در زمان رويداد يک شيء از جنس LowAmountEventArgs ايجاد مي کنم:

    [​IMG]

    حالا در زمان رويداد اين امکان وجود دارد که کاربر از ثبت اين برداشت از انبار جلوگيري کند. براي اين کار کافيست که کاربر شما در event handler مربوط به استفاده از متغيير موجود مقدار Cancel را به True ست کند.

    [​IMG]

    نمونه هاي بسياري از اين نوع رفتار ها در دات نت وجود دارد ، به عنوان مثال در کلاس Form در Windows Application وقتي درخواست بسته شدن فرم از طرف کاربر ارسال مي شود ، يک رويداد به نام FormClosing رخ مي دهد ، در صورتيکه شما يک EventHandler براي اين رويداد بنويسيد مي توانيد با ست کردن متغيير Cancel در کلاس FormClosingEventArgs مي توانيد مانع از بسته شدن فرم شويد.