מדריך C# מונחה עצמים

מדריך #C – מתקדם: נציגים – Delegates

‏ • John Bryce

נציגים (delegates) ואירועים (events) הינם נושאים מאד מרכזיים בעבודה ב- NET. . הם מאפשרים למפתח לבנות רכיב בלי להגביל את מי שמשתמש בו לפעולות מסוימות.

delegate הוא טיפוס המצביע על פונקציה.

תפקידו של ה- delegate הוא ליצור אובייקט המצביע על פונקציה. במקום להפעיל את הפונקציה בצורה הרגילה, נפעיל אותה דרך ה- delegate. היתרון של עבודה עם delegate הוא בהצבעה על פונקציה שאנו לא מכירים בזמן כתיבה של רכיב (מחלקה / פונקציה וכד') מסוים, ומי שיקבע איזו פונקציה תופעל הוא מי שמשתמש ברכיב.

להלן דוגמא שננתח אותה מיד:

 

namespace Delegates
{
  delegate int Calc(int x, int y);

  class Program
  {
    static int Add(int a, int b)
    {
      return a + b;
    }

    static void Main(string[] args)
    {
      Calc c= new Calc(Add);
      int result = c(4, 6);

      Console.WriteLine(result);
    }
  }
}

הגדרת delegate מתבצעת באמצעות המילה השמורה delegate ולאחריה חתימה של פונקציה, כאשר שם "הפונקציה" הוא למעשה שם ה- delegate.

להלן הגדרת delegate בשם Calc:

delegate int Calc(int x, int y);

 

delegate יכול להצביע רק על פונקציות שחתימתן היא בדיוק כמו חתימת ה- delegate.

ה- delegate (נציג) נקרא כך מיכוון שתפקידו "לייצג" פונקציות. בדוגמא שלנו ה- delegate יכול להצביע רק על פונקציות שמקבלות בדיוק שני פרמטרים מסוג int ומחזירות int. כלומר, הוא יוכל לייצג כל פונקציה שעונה לתנאים אלו בלבד – מקבלת שני מספרים שלמים (int) ומחזירה מספר שלם.

להלן דוגמא להצבעה על פונקציה בשם Add:

Calc c = new Calc(Add);

בכדי להפעיל את הפונקציה דרך ה- delegate יש להתייחס למשתנה שיצרנו כאילו הוא שם הפונקציה. בדוגמא שלהלן יודפס 10 (הסכום של 4,6):

int result = c(4, 6);
Console.WriteLine(result);

Multicast delegate

delegate יכול להצביע גם על מספר פונקציות, ניתן לעשות זאת באמצעות האופרטור =+ לדוגמא:

namespace Delegates
{
  delegate int Calc(int x, int y);

  class Program
  {
    static int Add(int a, int b)
    {
      Console.WriteLine("Add");
      return a + b;
    }

    static int Sub(int a, int b)
    {
      Console.WriteLine("Sub");
      return a - b;
    }

    static void Main(string[] args)
    {
      Calc c;
      c = new Calc(Add);
      c += new Calc(Sub);
      int result = c(4, 6);

      Console.WriteLine(result);
    }
  }
}

בפלט התוכנית ניתן לראות שהופעלו 2 הפונקציות אך התשובה שהוחזרה היא התשובה של הפונקציה האחרונה שהופעלה:

פלט - Add/Sub delegate

עד כאן הבנו איך יוצרים ומשתמשים ב- delegate להלן דוגמא הממחישה למה צריך delegate (מיד ננתח אותה):

namespace SortWithDelegate
{
  public delegate int CompareDeleg(int a, int b);

  class Program
  {
    static int[] arr;

    static int CompareAsc(int x, int y)
    {
      return x - y;
    }

    static int CompareDesc(int x, int y)
    {
      return y - x;
    }

    static void Sort(CompareDeleg compareMethod)
    {
      for (int i = 0; i < arr.Length - 1; i++)
        for (int j = i + 1; j < arr.Length; j++)
          if (compareMethod(arr[i], arr[j]) > 0)
            Replace(i, j);
    }

    private static void Replace(int i, int j)
    {
      int tmp = arr[i];
      arr[i] = arr[j];
      arr[j] = tmp;
    }

    static void PrintArr()
    {
      foreach (int num in arr)
        Console.Write(num + ",");

      Console.WriteLine();
    }

    static void Main(string[] args)
    {
      arr = new int[] { 123, 200, -63, 2, 7612, -13, 8 };
      Console.WriteLine("Orginal nuumbers:");
      PrintArr();

      Sort(new CompareDeleg(CompareAsc));
      Console.WriteLine("\nAscending order:");
      PrintArr();

      Sort(new CompareDeleg(CompareDesc));
      Console.WriteLine("\nDescending order:");
      PrintArr();
    }
  }
}

בתוכנית זו ישנה פונקציה בשם Sort שתפקידה למיין מערך מסוג []int. חישבו שהיינו רוצים למיין את המערך בצורות שונות פעם בסדר עולה ופעם בסדר יורד, עם הידע שיש לנו עד היום היינו צריכים לבנות 2 פונקציות, אחת למיון עולה ואחת למיון יורד. ואם בעתיד היינו רוצים שיטת מיון אחרת (חישבו למשל על תאריכים שאפשר למיין בשיטות שונות: שנים, רבעונים, חודשים ועוד).

הרעיון הוא שהפונקציה Sort תקבל את המערך ו- delegate בשם CompareDeleg שיטפל בצורת המיון, כך שהפונקציה לא תגביל לסוג מיון מסוים אלא תיתן למי שמפעיל אותה לקבוע את סוג המיון.

ה- delegate בנוי כך שהוא מקבל שני מספרים (int) ומחזיר מספר המייצג מי יותר גדול בצורה הבאה:

  • מספר חיובי – המספר הראשון יותר גדול
  • מספר שלילי – מספר השני יותר גדול
  • אפס – המספרים שווים

כעת כאשר נרצה להפעיל את הפונקציה נשלח אליה את המערך וכתובת של פונקציה אשר מגדירה את צורת המיון ובכל פעם נוכל לשלוח צורת מיון אחרת. ניתן לראות בדוגמא שישנן 2 פונקציות שכל אחת מהן מייצגת מיון בצורה אחרת:

  • CompareAsc – למיון עולה
  • CompareDesc – למיון יורד

בכל הפעלה של הפונקציה Sort אנו שולחים כתובת של פונקציה אחרת ולאחר מכן מדפיסים את המערך. בפלט התוכנית ניתן לראות שבכל הדפסה סדר המיון הוא שונה (המצב הראשוני, עולה, יורד):

פלט - Sorting delegate

תגיות: ,

ליאור זמיר

כיום אני ה- Webmaster של תוכנית החדשנות של HPE Software.לפני כן, הייתי מנהל תחום Webmaster ומרצה בכיר בג'ון-ברייס (במשך 9 שנים) בקורסים לפיתוח ותיכנות באמצעות Microsoft .NET, מולטימדיה, בניית אתרי אינטרנט ואינטראנט. פיתוח הדרכה ומתן ייעוץ טכנולוגי.

תגובות בפייסבוק