Make your own free website on Tripod.com

دروس مرتبط

 

  صفحه اصلی > آموزش #C > رخدادها و Delegate (دوم)

   

> معرفی #C

     آغاز کار با #C

     متغیرها،عبارات وعملگرها

     دستورهای کنترلی شرطی

     دستورهای کنترلی حلقه ای

 

> قالب برنامه ها

     کلاسها در #C

     آشنایی با NameSpace

     چگونگی نوشتن متد

     ساختارها (Structure)

 

> کار با کامپوننت ها

     ویژگی ها (Properties)

     صفات (Attribute)( اول)

     صفات (Attribute)( دوم)

     رخدادها و Delegate

     ( اول)

    رخدادها و Delegate

    ( دوم)

    رخدادها و Delegate

    ( سوم)

    کنترل خطاها

 

 
 

 رخدادها و delegate ها در   C#(بخش دوم)

فهرست مطالب بیان شده در این قسمت به قرار زیر است:

      

      درك سودمندي delegate ها

      حل مسئله بدون استفاده از delegate

      حل مسئله با استفاده از delegate

      اعلان delegate ها (بخش پيشرفته)

      فراخواني delegate ها (بخش پيشرفته)

      ايجاد نمونه‌هاي جديد از يك delegate (بخش پيشرفته)

درك سودمندي delegate ها

براي درك بهتر delegate ها به بررسي يك مثال مي‌پردازيم. در اينجا اين مثال را يكبار بدون استفاده از delegate و بار ديگر با استفاده از آن حل كرده و بررسي مي‌نمائيم. مطالب گفته شده در بالا نيز به نحوي مرور خواهند شد. توجه نماييد، همانطور كه گفته شد delegate ها و رخدادها بسيار با يكديگر در تعامل‌اند، از اينرو در برخي موارد به ناچار از رخدادها نيز استفاده شده است. رخدادها در قسمت انتهايي اين درس آورده شده‌اند، از اينرو در صورتيكه در برخي موارد دچار مشكل شديد و يا درك مطلب برايتان دشوار بود، ابتدا كل درس را تا انتها مطالعه نماييد و سپس در بار دوم با ديدي جديد به مطالب و مفاهيم موجود در آن نگاه كنيد. در اغلب كتابهاي آموزشي زبان C# نيز ايندو مفهوم با يكديگر آورده شده‌اند ولي درك رخدادها مستلزم درك و فراگيري كامل delegate هاست، از اينرو مطالب مربوط به delegate ها را در ابتدا قرار داده‌ام.

ابتدای صفحهF

حل مسئله بدون استفاده از delegate

فرض كنيد، ميخواهيد برنامه بنويسيد كه عمل خاصي را هر يك ثانيه يكبار انجام دهد. يك روش براي انجام چنين عملي آنست كه، كار مورد نظر را در يك متد پياده‌سازي نماييد و سپس با استفاده از كلاسي ديگر، اين متد را هر يك ثانيه يكبار فراخواني نمائيم. به مثال زير توجه كنيد :

class Ticker
{
    
    public void Attach(Subscriber newSubscriber)
    {
        subscribers.Add(newSubscriber);
    }
    public void Detach(Subscriber exSubscriber)
    {
        subscribers.Remove(exSubscriber);
    }
    // هر ثانيه فراخواني ميگردد Notify 
    private void Notify()
    {
        foreach (Subscriber s in subscribers)
        {
            s.Tick();
        }
    }
 
    private ArrayList subscribers = new ArrayList();
}
class Subscriber
{
    public void Tick()
    {
     
    }
}
class ExampleUse
{
    static void Main()
    {
        Ticker pulsed = new Ticker();
        Subscriber worker = new Subscriber();
        pulsed.Attach(worker);
         }
}

اين مثال مطمئناً كار خواهد كرد اما ايدآل و بهينه نيست. اولين مشكل آنست كه كلاس Ticker بشدت وابسته به Subscriber است. به بيان ديگر تنها نمونه‌هاي جديد كلاس Subscriber مي‌توانند از كلاس Ticker استفاده نمايند. اگر در برنامه كلاس ديگري  داشته باشيد كه بخواهيد آن كلاس نيز هر يك ثانيه يكبار اجرا شود، مي‌بايست كلاس جديدي شبيه به Ticker ايجاد كنيد. براي بهينه كردن اين مسئله مي‌توانيد از يك واسط (Interface) نيز كمك بگيريد. براي اين منظور مي‌توان متد Tick را درون واسطي قرار داد و سپس كلاس Ticker را به اين واسط مرتبط نمود.

interface Tickable
{
    void Tick();
}
 
class Ticker
{
    public void Attach(Tickable newSubscriber)
    {
        subscribers.Add(newSubscriber);
    }
    public void Detach(Tickable exSubscriber)
    {
        subscribers.Remove(exSubscriber);
    }
    // هر ثانيه فراخواني ميگردد Notify
    private void Notify()
    {
        foreach (Tickable t in subscribers)
        {
            t.Tick();
        }
    }
    
    private ArrayList subscribers = new ArrayList();
}

 

اين راه حل اين امكان را براي كليه كلاسها فراهم مي‌نمايد تا واسط Tickable را پياده‌سازي كنند.

class Clock : Tickable
{
      public void Tick()
    {
      
    }
   }
class ExampleUse
{
    static void Main() 
    {
        Ticker pulsed = new Ticker();
        Clock wall = new Clock();
        pulsed.Attach(wall);
        
    }
}

حال به بررسي همين مثال با استفاده از delegate خواهيم پرداخت.

ابتدای صفحهF

حل مسئله با استفاده از delegate

استفاده از واسطها در برنامه‌ها، مطمئناً روشي بسيار خوب است، اما كامل نبوده اشكالاتي دارد. مشكل اول آنست كه اين روش بسيار كلي و عمومي است. تصور نماييد مي‌خواهيد از تعداد زيادي از سرويسها استفاده نماييد(بعنوان مثال در برنامه‌هاي مبتني بر GUI. در اينگونه برنامه‌ها هجم عظيمي از رخدادها وجود دارند كه مي‌بايست با تمامي آنها در ارتباط باشيد.) مشكل ديگر آنست كه استفاده از واسط، بدين معناست كه متد Tick بايد متدي public باشد، از اينرو هر كدي مي‌تواند Clock.Tick را در هر زماني فراخواني نمايد. روش مناسب تر آنست كه مطمئن شويم تنها اعضايي خاص قادر به فراخواني و دسترسي به Clock.Tick هستند. با استفاده از delegate تمامي اين امكانات براي ما فراهم خواهد شد و برنامه‌هايي با ايمني بالاتر و پايدارتر مي‌توانيم داشته باشيم.

ابتدای صفحهF

 اعلان Delegate

در مثال ما، متد Tick از واسط Tickable از نوع void بود و هيچ پارامتري دريافت نمي‌كرد :

interface Tickable
{
    void Tick();
}

براي اين متد مي‌توان delegate ي تعريف نمود كه ويژگيهاي آنرا داشته باشد :

delegate void Tick();

 همانطور كه قبلاً نيز گفته شد، اين عمل نوع جديدي را ايجاد مي‌نمايد كه مي‌توان از آن همانند ساير انواع استفاده نمود. مثلاً مي‌توان آنرا بعنوان پارامتري براي يك متد در نظر گرفت :

void Example(Tick param)
{
    
}

ابتدای صفحهF

فراخواني delegate

قدرت و توانايي delegate زماني مشهود مي‌گردد كه مي‌خواهيد از آن استفاده نماييد. براي مثال، با متغير param در مثال قبل چكار مي‌توانيد انجام دهيد؟ اگر param متغيري از نوع int بود، از مقدار آن استفاده مي‌كرديد و با استفاده از عملگرهايي نظير +، - و يا عملگرهاي مقايسه‌اي، عملي خاص را بر روي آن انجام مي‌داديد. اما حال كه param متغيري از نوع int نيست، چه مي‌كنيد؟ متغير param يك delegate است و همانطور كه گفته شد، delegate انتزاعي از يك متد است، پس هر عملي كه متد انجام مي‌دهد، delegate نيز مي‌تواند انجام دهد. با استفاده از پرانتز، مي‌توان از delegate استفاده نمود :

void Example(Tick param)
{
    param();
}

نكته : همانطور كه اشاره شد، delegate يكي از انواع مرجعي است از اينرو مقدار آن مي‌تواند برابر با Null باشد. در مثال فوق، اگر مقدار param برابر با Null باشد، كامپايلر خطاي NullReferenceException را ايجاد مي‌نمايد.

همانند متدها، delegate ها بايد بطور كامل و صحيح فراخواني گردند. با توجه به اعلان Tick، در زمان فراخواني  اين delegate، مثلاً param، بايد توجه داشت كه هيچ پارامتري را نمي‌توان به آن ارسال نمود و نمي‌توان آنرا به متغيري نسبت داد چراكه اين delegate بصورت void اعلان شده و مقدار بازگشتي ندارد.

void Example(Tick param)
{
    param(42);                  // خطاي زمان كامپايل رخ مي‌دهد
    int hhg = param();          // خطاي زمان كامپايل رخ مي‌دهد
    Console.WriteLine(param()); // خطاي زمان كامپايل رخ مي‌دهد
}

توجه نماييد كه delegate را به هر نحوي مي‌توانيد اعلان نماييد. براي مثال به نسخة ديگري از Tick توجه كنيد :

delegate void Tick(int hours, int minutes, int seconds);

اما به ياد داشته باشيد كه همانند متد، در هنگام استفاده از آن بايد پارامترهاي صحيح به آن ارسال نماييد :

void Example(Tick method)
{
    method(12, 29, 59);
}

با استفاده از delegate مي‌توانيد كلاس Ticker را پياده‌سازي كنيد :

delegate void Tick(int hours, int minutes, int seconds);

class Ticker

{

   

    public void Attach(Tick newSubscriber)

    {

        subscribers.Add(newSubscriber);

    }

    public void Detach(Tick exSubscriber)

    {

        subscribers.Remove(exSubscriber);

    }

 

    private void Notify(int hours, int minutes, int seconds)

    {

        foreach (Tick method in subscribers)

        {

            method(hours, minutes, seconds);

        }

    }

   

    private ArrayList subscribers = new ArrayList();

}

Fابتدای صفحه

ساخت نمونه‌هاي جديد از يك delegate

آخرين كاري كه بايد انجام دهيد، ايجاد نمونه‌هاي جديد از delegate ساخته شده است. يك نمونة جديد از يك delegate، تنها انتزاعي از يك متد است كه با نامگذاري آن متد ايجاد مي‌شود.

class Clock
{
   
    public void RefreshTime(int hours, int minutes, int seconds)
    {
       
    }
    
}

با توجه به ساختار Tick، ملاحظه مي‌نماييد كه متد RefreshTime كاملاً با اين delegate همخواني دارد :

delegate void Tick(int hours, int minutes, int seconds);

و اين بدين معناست كه مي‌توان نمونة جديد از Tick ايجاد كرد كه انتزاعي از فراخواني RefreshTime در شيء خاصي از Clock است.

Clock wall = new Clock();
 
Tick m = new Tick(wall.RefreshTime);

حال كه m، ايجاد شد، مي‌توانيد از آن بصورت زير استفاده نماييد :

m(12, 29, 59);

اين دستور در حقيقت كار دستور زير را انجام مي‌دهد (چون m دقيقاً انتزاع آن است) :

wall.RefreshTime(12, 29, 59);

همچنين مي‌توانيد m را بعنوان پارامتر به متدي ارسال نماييد. حال تمام چيزهايي را كه براي حل مسئله با استفاده از delegate بدانها نياز داشتيم را بررسي كرديم.  در زير مثالي را مشاهده مي‌كنيد كه كلاسهاي Ticker و Clock را به يكديگر مرتبط نموده است. در اين مثال از واسط استفاده نشده و متد RefreshTime، متدي private است :

delegate void Tick(int hours, int minutes, int seconds);

 

class Clock

{

   

    public void Start()

    {

        ticking.Attach(new Tick(this.RefreshTime));

    }

 

    public void Stop()

    {

        ticking.Detach(new Tick(this.RefreshTime));

    }

 

    private void RefreshTime(int hours, int minutes, int seconds)

    {

        Console.WriteLine("{0}:{1}:{2}", hours, minutes, seconds);

    }

 

    private Ticker ticking = new Ticker();

}

با اندكي تامل و صرف وقت مي‌توانيد delegate  را بطور كامل درك نماييد.

ابتدا صفحهF  

Back Next

  صفحه اصلی
    C# آموزش
    مفاهیم شی گرایی
    برنامه های نمونه
    کار با محیط
    معرفی کتاب
    نقشه سایت
    درباره ما
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
                               
[صفحه اصلی] [آموزش #C] [مفاهیم شی گرایی] [برنامه های نمونه] [کار با محیط] [معرفی کتاب] [نقشه سایت] [درباره ما]