יום חמישי, 3 באוקטובר 2013

Delegate, Action, Func ומה שבינהם

חבר בעבודה שאל אותי השבוע: מה זה Func?

אז בשביל להסביר לו, עניתי לו שזה פשוט צורה יותר קלה להגדרת Delegate.

אז נתחיל מההתחלה: מה זה Delegate?

 Delegate זה אובייקט Reference שמצביע לפונקציה.היתרון בשימוש ב Delegate זה שצד שלישי יכול להגדיר איזה פונקציה הוא בדיוק רוצה שתופעל, והמימוש שלנו לא יהיה תלוי בכך.

הגדרת Delegate:

הגדרת ה Delegate תהיה תחת ה namespace שלנו, ולא תחת Class מסוים. לדוגמא:

delegate void PrintDelegate(string st);

מה הגדרנו כאן: הגדרנו שה Delegate שלנו יכול להצביע לכל פונקציה שהחתימה שלה: מחזירה Void ומקבלת String.

נאמר שיש לנו בתוכנית את שתי הפונקציות הנ"ל:

static void PrintOne(string st)
        {
            System.Console.WriteLine("{0} is in PrintOne Function", st);
        }

        static void PrintTwo(string st)
        {
            System.Console.WriteLine("{0} is in PrintTwo Function", st);
        }

לאחר שנריץ את הקוד הבא:

static void Main(string[] args)
        {
            PrintDelegate p = new PrintDelegate(PrintOne);
            
            p("Eyal");
        }

נקבל:
Eyal is in PrintOne Function

בשורה מספר 3 הגדרנו שה Delegate שלנו יצביע לפונקציה PrintOne, ובשורה 5 פשוט קראנו לDelegate שלנו בדיוק כמו שהיינו קוראים אם היינו קוראים לפונקציה.

Multycast Delegate

Delegate יכול להצביע גם על מספר פונקציות (נקרא Multycast Delegate). וכאשר "מפעילים" את ה Delegate, הוא יקרא לפונקציות בסדר שהם נרשמו. את הרישום נעשה באמצעות אופרטור =+

למשל:

static void Main(string[] args)
        {
            PrintDelegate p = new PrintDelegate(PrintOne);
            p += PrintTwo;
            p("Eyal");
        }


ונקבל:
Eyal is in PrintOne Function
Eyal is in PrintTwo Function

Closure

שימוש נפוץ ל Delegate הוא ביצוע Closure, כלומר העברת פונקציה כפרמטר.

נניח ויש לנו את הפונקציה הנ"ל:

static void MyPrintMethod(PrintDelegate p)
        {
            p("MyPrintMethod");
        }

הפונקציה מקבלת כפרמטר Delegate ומפעילה אותו.

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

MyPrintMethod(new PrintDelegate(PrintTwo));

ונקבל:
MyPrintMethod is in PrintTwo Function

Generic Delegate

אנחנו יכולים גם להגדיר שה Delegate שלנו יהיה גנרי, ואז לא נצטרך לרשום Delegate שונים עבור פונקציות שמקבלות Type שונים:

לשם ההדגמה נניח ויש לנו פונקציית הדפסה חדשה:


static void PrintThree(int num)
        {
            System.Console.WriteLine("{0} is in PrintThree Function", num);
        }

עכשיו תחת ה namespace נגדיר את ה Delegate הגנרי הבא:

delegate void PrintGenericDelegate(T t);

וכעת נוכל להשתמש עם אותו ה Delegate שיצביע על פונקציות עם חתימות שונות:



PrintGenericDelegate<string> pgs = new PrintGenericDelegate<string>(PrintTwo);

 pgs("Eyal");

 PrintGenericDelegate<int> pgi = new PrintGenericDelegate<int>(PrintThree);

 pgi(111);
ונקבל:
Eyal is in PrintTwo Function
111 is in PrintThree Function

<Action<T

יופי, אז ראינו שימושים שונים ל Delegate.
אבל זה קצת מסורבל, לא?

החל מ NET 2.0 קיבלנו את Action, שזהו פשוט סוג של Delegate, שיודע לקבל עד 16 פרמטרים, ומחזיר void. השימוש ב Action מקל לנו על כתיבת הקוד.

תראו איזה פשוט נהיה השימוש ב Delegate, באמצעות Action:
אין צורך להגדיר Delegate ב namespace, אלא פשוט יוצרים Action, ונותנים לו הצבעה לפונקציה:


Action<string> MyAction = PrintTwo;

 MyAction("action");

ונקבל:
action is in PrintTwo Function

ונראה איזה פשוט זה לשלוח פונקציה כפרמטר באמצעות Action (ביצוע Closure):

נניח ויש לנו את הפונקציה:

static void MyPrintMethodAction(Action<string> action)
        {
            action("MyPrintMethodAction");
        }

נפעיל את הקריאה ע"י:

Action<string> MyAction = PrintTwo;

MyPrintMethodAction(MyAction);

ונקבל:
MyPrintMethodAction is in PrintTwo Function

<Func<T,Result

ולשאלתנו, מה זה Func? זה פשוט כל מה ש Action, רק ש Func מחזיר פרמטר.

יש לנו לדוגמא את הפונקציה הפשוטה הבאה שמקבלת פרמטר מסוג int, ומחזירה int:

static int MultiplyTwice(int num)
        {
            return num * 2;
        }


נשתמש עם ה Delegate הבא:

Func<int,int> MyFunc = MultiplyTwice;

Console.WriteLine(MyFunc(4));


ונקבל: 8

שימו לב שב Func, הפרמטר הגנרי האחרון יהיה הטיפוס שאנחנו מחזירים. מכיוון ש Func תמיד מחזיר ערך אז Func T לא מקבל שום פרמטר, אך מחזיר פרמטר מסוג T.

תהנו!!

אין תגובות:

הוסף רשומת תגובה