DCSIMG
C# 4.0 Part 1 (First glance on the dynamic) - שלמה גולדברג (הרב דוטנט)

שלמה גולדברג (הרב דוטנט)

מרצה בסלע ויועץ בעולם ה - net.

C# 4.0 Part 1 (First glance on the dynamic)

 

כזכור לא מזמן התפרסם ב -  MSDN מאמר "מה חדש ב - C# 4.0" וזה גם היה מאמר החודש ב - MSDN Pulse.
 
אני לומד את C# 4.0 יותר לעומק (עבור הרצאה שאני מתכוון להעביר), ואני מתכוון לכתוב סדרת פוסטים שיכנסו יותר לעומק של התכונות החדשות של השפה.
 
 
אחד מהחידושים הגדולים של השפה (שיספק לי חומר לכמות פוסטים נכבדה) הוא ה - DLR (Dynamic Language Runtime
 
אנחנו נחקור מה הוא עושה מאחורי הקלעים בפוסטים הבאים, כרגע נראה מה הוא נותן לנו לעשות (איך הוא עושה את זה, נדבר בהזדמנות אחרת)
 
 
 

    dynamic age = 23;

    dynamic name = "Shlomo";

    dynamic children = new List<string>() { "yossi", "meir", "rivka" };

 

 

    Console.WriteLine(++age);

 

    children.Add("shara");

 

 

    foreach (var item in children)

    {

        Console.WriteLine(item);

    }

 
 
כמו שאנחנו רואים, יש לנו מילת מפתח חדשה בשם "dynamic" שיכולה לקבל כל דבר.
 
זה לא var, ההבדל המהותי ביניהם ש - var מקבל את ה - type האמיתי שלו בזמן קומפילציה, לעומת זאת dynamic מקבלת לכאורה את ה - type האמיתי בזמן ריצה (למה לכאורה, נדבר בפוסט אחר).
 
אפשר להכניס כל type ל - dynamic, ולהפעיל כל מתודה או מאפיין שהיינו יכולים להפעיל על הטיפוס האמיתי, אבל אין לנו intelicence, הבדיקה האם באמת המאפיין או המתודה קיימים לאותו אובייקט, יתבצעו בזמן ריצה.
 
 
השאלה המתבקשת מה זה נותן לנו, לפניכם דוגמא אחת:
 
נניח שאנחנו רוצים לכתוב מתודה שיודעת לקבל מספרים מסוג int  ולהחזיר את סכומם, מן הסתם נכתוב קוד כזה:
 
 

   public static int Sum(params int[] values)

    {

        int sum = 0;

        for (int i = 0; i < values.Length; i++)

        {

            sum += values[i];

        }

 

        return sum;

    }

 
מה יקרה עם נרצה double, נצטרך לכתוב את המתודה הבאה:
 
 

    public static double Sum(params double[] values)

    {

        double sum = 0;

        for (int i = 0; i < values.Length; i++)

        {

            sum += values[i];

        }

 

        return sum;

    }

 
 
אבל מה יקרה עם נרצה לכתוב מתודה שיודעת לקבל כל סוג שמממש את האופרטור += (כמו DateTime) האם נצטרך לכתוב מתודה עבור כל סוג ?
(אי אפשר עם Generic כי אין לנו constrain על אופרטור =+)
 
עם dynamic לא נצטרך:
 
 

    public static dynamic Sum(params dynamic[] values)

    {

        dynamic sum = values[0];

        for (int i = 1; i < values.Length; i++)

        {

            sum += values[i];

        }

 

        return sum;

    }

 
לא משנה איזה טיפוס נשלח לפונקציה, כל עוד שהוא יודע לעשות =+, הפונקציה תעבוד, לדוגמא
 
 

    Sum(2, 3, 4);

 

    Sum("a", "b", "c“);

 

    Sum(DateTime.Now, new TimeSpan(3, 3, 3));

 
יחזיר (בהתאמה)
 
           9
 
           abc
 
           15/06/2009 00:27:43
 
 
 
בואו נראה עוד דוגמא שימושית ל - dynamic:
 
כשאני מלמד interface, אחד הדוגמאות השימושיות הוא - למיין כל סוג של מערך באמצעות IComparable.
 
נניח שיש לי מערך של int ואני רוצה למיין אותו, מן הסתם יהיה לי קוד כזה:
 
 

    public static void Sort(int[] array)

    {

        for (int i = 0; i < array.Length; i++)

        {

            for (int j = 0; j < array.Length - 1; j++)

            {

                if (array[j] > array[j + 1])

                {

                    int tmp = array[j];

                    array[j] = array[j + 1];

                    array[j + 1] = tmp;

                }

            }

        }

    }

 
 
מה יקרה עם יש לי מערך של person, שזה מציב לפנינו שני בעיות, הראשונה שהפונקציה מקבלת מערך של int והשנייה לפי מה נעשה את ההשוואה האם צריך להחליף את המיקום של האובייקטים במערך.
 
את הבעייה הראשונה אפשר לפתור באופן פשוט הפונקציה תקבל אובייקט מסוג Array (האבא של כל המערכים),
את הבעייה השנייה נפתור באמצעות IComparable, והקוד שלנו יראה כך:
 

    public static void Sort(Array array)

    {

        for (int i = 0; i < array.Length; i++)

        {

            for (int j = 0; j < array.Length - 1; j++)

            {

                IComparable comparable = (IComparable)array.GetValue(j);

                if (comparable.CompareTo(array.GetValue(j + 1)) == 1)

                {

                    object tmp = array.GetValue(j);

                    array.SetValue(array.GetValue(j + 1), j);

                    array.SetValue(tmp, j + 1);

                }

            }

        }

    }

 
זוהי דוגמא נהדרת להסבר על interface, מה שמעצבן של - Array אין מימוש של indexer, (למעשה יש, אבל הוא מוגדר כ - internal) מה שגורם לקוד להיראות מגעיל עם כל ה - GetValue וה - SetValue.
 
בואו נעביר את הקוד ל - dynamic
 
 

    public static void Sort(Array array)

    {

        dynamic dynamicArray = array;

 

        for (int i = 0; i < array.Length; i++)

        {

            for (int j = 0; j < array.Length - 1; j++)

            {

                if (dynamicArray[j].CompareTo(dynamicArray[j + 1]) == 1)

                {

                    dynamic tmp = dynamicArray[j];

                    dynamicArray[j] = dynamicArray[j + 1];

                    dynamicArray[j + 1] = tmp;

                }

            }

        }

    }


הופה, תראו איזה קוד נקי יצא לנו, אנחנו מקבלים Array, בתוך הפונקציה אנחנו מסתכלים על זה באמצעות dynamic, ואנחנו יכולים לגשת ל - indexer, וכמובן לגשת למתודת ה - CompareTo מבלי צורך בהמרה ל - IComparable.
 
 
 
בפוסט הבא אני אדגים איך אפשר לכתוב מימוש של מחשבון באובייקט דוט נטי, ומימוש של מחשבון ב - Javascript, נבנה WinForm שיקבל אחד משני המימושים (או את האובייקט הדוט נטי או את אובייקט ה - Javascript עטוף באובייקט Com) ויוכל להפעיל את הפונקציות ללא צורך ב - Reflection.
פורסם: Jun 18 2009, 09:02 AM by Shlomo | with 8 comment(s)
תגים:, , , ,

תוכן התגובה

שלמה גולדברג כתב/ה:

אז כמו שהבטחתי בפוסט הקודם אני אדגים שימוש אמיתי ב - dynamic, אפשר להוריד את התוכנית מכאן (נפתח ב VS2010

# June 19, 2009 12:42 PM

שלמה גולדברג כתב/ה:

שני הפוסטים הקודמים עסקו בהבנה איזה שימוש אפשר לעשות עם ה - dynamic. הראשון היה הדגמה פשוטה של היכולות

# June 20, 2009 10:53 PM

Overflow כתב/ה:

כל הכבוד על ההשקעה...

# June 30, 2009 8:51 AM

שלמה גולדברג כתב/ה:

נראה לי שדי סיכמתי את החידושים ב - C# 4.0, אמנם יש עוד כמה חידושים אבל הם פחות מרכזיים אז חשבתי שכדאי

# July 2, 2009 11:35 PM

דני כתב/ה:

את הדוגמא הראשונה נראה לי הרבה יותר הגיוני אם היה אפשר לעשות ככה:

  public static T Sum(params T[] values)

where T : primitive number

   {

       T sum = 0;

       for (int i = 0; i < values.Length; i++)

       {

           sum += values[i];

       }

       return sum;

   }

ראשית, dynamic לא בודק האם מדובר במספר בזמן קומפילציה. שנית, אין לך intelicence.

בקשר לדוגמא עם ה- Array לא ברור לי למה ה- indexer מוגדר כ- internal, וגם אם כן אז למה דרך dynamic זה כן עובד? משהו נשמע לי פה לא הגיוני..

# July 4, 2009 11:37 AM

Shlomo כתב/ה:

היה נחמד מאוד אם היה לנו constrain שנקרא primitive numbers, אבל מה לעשות שאין.

הנקודה השנייה, הסיבה שה - indexer עובד היא לא בגלל שזה internal (אם זה היה הסיבה זה באמת לא היה עובד)

זה עובד כי dynamic מתייחס לאובייקט ב - runtime ולאותו אובייקט (שהוא כזכור מערך כלשהו - נניח של int) יש indexer שמוגדר כ - public

# July 4, 2009 10:47 PM

ליאור חדשיאן כתב/ה:

תודה רבה על ההסבר =]

# July 4, 2010 4:25 PM
שלח תגובה

(שדה חובה)  

(שדה חובה)  

(אופציונלי)

(שדה חובה) 

Please add 2 and 4 and type the answer here:


Enter the numbers above: