DCSIMG
June 2009 - Posts - שלמה גולדברג (הרב דוטנט)

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

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

June 2009 - Posts

C# 4.0 Part 11 - Named Parameters

 

ל - Optional Parameters יש בעיה אחת קטנה.
 

    static void Add(int id, string name = "shlomo", float age = 0)

    {

    }

 

    static void Main()

    {

        Add(22, ,24);

    }

 
 
זה לא מתקמפל וכמובן מאוד לא ברור. ולכן המציאו את ה - Name Parameters.
 

    static void Main()

    {

        Add(22, age: 24);

    }

 
 
גם עבור פרמטרים שהם חובה ניתן לשלוח עם השם:
 

    static void Main()

    {

        Add(id: 22, age: 24);

    }

 

זה גורם שהקוד שלנו יהיה יותר קריא. אבל צריך להיות זהירים עם השימוש ב - Named Parameters. (פוסט מומלץ לקריאה - כאן).
החל מ - C# 4.0 אסור לשנות את השמות של פרמטרים של מתודות או של Interface מכיון שזה יכול לגרום לשגיאות קומפילציה של מי שקרא למתודה שלנו והשתמש ב - Named Parameter וברגע שנשנה את השם של הפרמטר הקוד שלו לא יתקמפל, סיבה שנייה - שהתוצאה שאנחנו מצפים לקבל יכולה להשתנות לפי הסדר של שליחת הפרמטרים, לדוגמא:
 

        static void Main()

        {

            int i = 0;

            Print(i++, i++, i++);

 

            i = 0;

            Print(z: i++, y: i++, x: i++);

 

        }

 

        static void Print(int x, int y, int z)

        {

            Console.WriteLine("x {0}, y {1}, z {2}", x, y, z);

        }

 
שימו לב להבדל בתוצאות בין הקריאה הראשונה לשנייה.
 
Naemd Parameters
 
 
ושוב תמיד צריך לזכור שכל העבודה זה של הקומפיילר שעובד קשה כדי להכניס את הערכים למשתנים כמו שבקשנו.
 
 
נקודה נוספת לזהירות בשימוש עם Named Parmetser היא חוסר היכולת שלו לעבוד עם params כלומר, ברגע שנקרא לפונקציה ונשלח אפילו את אחד מהפרמטרים באמצעות השם שלו, לא נוכל לשלוח ערכים לפרמטר מסוג params, הקוד הבא (של פונקצית ה - Main) אינו חוקי.
 

    static void Add(int id, params int[] intArray)

    {

    }

 

    static void Main()

    {

        Add(id: 10, intArray: 2, 2, 2); 

    }

 
זה נראה לי כמו באג ולא כמו משהו שתכננו מראש לעשות את זה, יותר מכך אני ציפיתי שבגלל שיש Named Parameters נוכל להגדיר מתודה שמקבלת יותר מ - params אחד, כלומר ציפיתי שהקוד הבא יהיה חוקי.
 

    static void Add(params string[] stringArray, params int[] intArray)

    {

    }

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

    static void Main()

    {

        Add(stringArray: "a", "b", "c", intArray: 2, 3, 4); 

    }

 
אבל לדאבוני זה לא עובד, ולכן נזכור שברגע שאנחנו משתמשים ב - Named Parameters לא נוכל לשלוח ערכים לפרמטר מסוג params ויותר מכך ברגע שאנחנו מגדירים פרמטר שהוא אופציונלי, במידה ולא ישלחו לו ערך לא נוכל לקבל גם עבור ה - params, לדוגמא:
 

    static void Add(string name, int id = 10, params int[] intArray)

    {

    }

 
כדי לשלוח ערך ל - intArray נהיה חייבים לשלוח ערך גם ל - id (ולא באמצעות השם שלו, אלא באמצעות המיקום שלו)
 
 
 
הסיבה שמשתמשים עם האופרטור : ולא עם האופרטור = היא כדי לא לבלבל עם משתנים של ה - class, שמו לב לקוד (וזה דרך אגב גם יעבוד בגרסאות קודמות של השפה)
 

    public class Class1

    {

        static void Main()

        {

            new Person().Run();

        }

    }

 

    class Person

    {

        private int id = 20;

 

        public void Run()

        {

            Console.WriteLine(this.id);

            Add(id = 10);

            Console.WriteLine(this.id);

        }

 

        void Add(int id)

        {

            Console.WriteLine(id);

        }

    }

 
זה מוזר, אבל מסתבר שהקריאה למתודה Add עם שליחת הפרמטר בצורה הבאה: id = 10 היא חוקית גם לפני C# 4.0, (בתנאי שהוגדר גם משתנה בשם הזה כמשתנה גלובלי) ומה שזה עושה זה באמת לתת ערך ל - id של המתודה Add אבל במקביל זה גם משנה את הערך של המשתנה id הגלובלי שהערך שלו היה מוגדר ל - 20. התוצאה של הריצה תדפיס:
 
20
10
10
 
וכדי שזה לא יקרה החליטו להשתמש עם האופרטור : כדי לשלוח ערכים לפרמטרים.

C# 4.0 Part 10 - Optional Parameters - Behind The Scenes

 

פספסתי את הפוסט של מוצאי שבת, (אולי אני אכתוב היום שניים).
 
בכל מקרה אנחנו רוצים לדעת מה קורה מאחורי הקלעים כשמשתמשים ב - Optional Parameters.
 
למעשה מייקרוסופט משתמשים בשני Attributes שקיימים כבר המון זמן. הראשון הוא - System.Runtime.InteropServices.OptionalAttribute והשני הוא - System.Runtime.InteropServices.DefaultParameterValueAttribute, למעשה הקוד הבא:
 

    static void Add(int id, string name = "", float age = 0)

    {

    }

 
מתרנדר לקוד הבא:
 

    static void Add(int id,

                    [Optional, DefaultParameterValue("")] string name,

                    [Optional, DefaultParameterValue(0)] float age)

    {

    }

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

    static void Add(DateTime dt1 = new DateTime(),

                    DateTime dt2 = default(DateTime),

                    int i = 0,

                    Person p = null)

    {

    }

 
 
אפשר גם לכתוב את הקוד הבא:
 

    static void Add1([Optional] DateTime dt1,

                    [Optional] DateTime dt2,

                    [Optional] int i,

                    [Optional] Person p)

    {

    }

 
 
שימוש רק ב - Attribute של Optional ללא שימוש ב - DefaultParameterValue נותן את ערך ברירת המחדל.
 
 
מה זה נותן לנו שאנחנו יודעים שאפשר להשתמש ישירות ב - attribute במקום לכתוב ערך כלשהו.
קודם כל אני חושב שתמיד חשוב לדעת איך הדברים עובדים מאחורה, שנית ישנם כמה דברים שאפשר לעשות רק עם ה - attributes ולא באמצעות השמה.
 
הראשון - בהגדרה של מתודה חייבים לכתוב קודם את כל הפרמטרים שהם חובה, כלומר הקוד הבא אינו חוקי
 

    static void Add(string name = "shlomo", int id)

    {

    }

 
ויחזיר את הודעת השגיאה:
 
Optional parameters must appear after all required parameters
 
לעומת זאת הקוד הבא:
 

    static void Add([Optional, DefaultParameterValue("shlomo")]string name,

                    int id)

    {

    }

 
יעבור קומפילציה ואפילו יעבוד, אני לא אומר שזה שימושי כל כך, אבל טוב לדעת שזה אפשרי.
 
 
שנית - וזה כבר יותר שימושי, זהו שימוש ב - dynamic.
 
הקוד הבא:
 

    static void Add(dynamic d = 10)

    {

    }

 
יחזיר שגיאת קומפילציה :
 
'd' is of type 'dynamic'. A default parameter value of a reference type other than string can only be initialized with null
 
אנחנו יודעים כמובן ש - dynamic הוא לא reference type. (נראה כמו באג של הקומפיילר) לעומת זאת הקוד הבא:
 

    static void Add([Optional, DefaultParameterValue(10)]dynamic d)

    {

    }

 
יעבור קומפילציה וכמובן נוכל לשלוח ל - d כל ערך שנרצה.
 
 
נקודה חשובה אחת, שצריך לזכור שאין קסמים ב - Runtime והכל זה עבודה קשה של הקומפיילר, כלומר הקוד הבא:
 
   

    public static void Main()

    {

        Add("noam");

        Add();

    }

 

    static void Add(string name = "shlomo")

    {

    }

 
 
מתרנדר לקוד הבא:
 

    public static void Main()

    {

        Add("noam");

        Add("shlomo");

    }

 

    static void Add(string name = "shlomo")

    {

    }

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

C# 4.0 Part 9 - Spell Checking with word using Optional Parameters

 

 כמו שהבטחתי אני רוצה להדגים שימוש ב - Optional Parameters.
 
 
הקוד הבא משתמש בבדיקת האיות של Word.
 
 

    public static void Main()

    {

        Application word = new Application();

 

        object missingValue = Type.Missing;

       

        Document doc = word.Documents.Add(ref missingValue, ref missingValue,

                                        ref missingValue, ref missingValue);

        doc.Activate();

 

        SpellingSuggestions suggestions =

            word.GetSpellingSuggestions("placa",

                                ref missingValue, ref missingValue,

                                ref missingValue, ref missingValue,

                                ref missingValue, ref missingValue,

                                ref missingValue, ref missingValue,

                                ref missingValue, ref missingValue,

                                ref missingValue, ref missingValue,

                                ref missingValue);

 

 

        foreach (SpellingSuggestion sg in suggestions)

        {

            Console.WriteLine(sg.Name);

        }

    }

 
 
כדי לא להשאיר באפלה את מי שלא מבין את הקוד הזה, אני אתן הסבר קצר.
 
בהתחלה אני יוצר מופע של Word Application, אני מוסיף לו מסמך (כי אני לא יכול להשתמש בבודק האיות אם אין מסמך), אבל היות שהמתודה Add מקבלת כל מיני פרמטרים (החל מתבנית ועד האם להציג את המסמך) אני שולח את הערך Type.Missing שכמשמעותו - אובייקט חסר ערך ובמילים אחרות אני אומר לפונקציה להשתמש בערך ברירת המחדל עבור הפרמטרים.
 
לאחר מכן אני מפעיל את המתודה GetSpellingSuggestions שמקבלת מילה לבדיקה ועוד שלוש עשרה פרמטרים שונים ומשונים (ותאמינו לי מי שנתקל פעם ראשונה בזה:
 
 
Image 
 אין לו מושג מה צריך לשלוח).
 
 
בסוף אני מדפיס את ההצעות שקבלתי.
 
Spelling Suggestion
 
 
נחזור לעניין. גם המתודה Add וגם המתודה GetSpellingSuggestion הפרמטרים שלהם מוגדרים כפרמטרי רשות (אם תעשו את זה ב - VB.Net תראו) אבל היות שעד ל C# 4.0 השפה לא תמכה בפרמטרי רשות, הקוד היה נראה זוועה.
 
 
שימו לב לאותו קוד ב - C# 4.0
 

    public static void Main()

    {

        Application word = new Application();

 

        Document doc = word.Documents.Add();

        doc.Activate();

 

        SpellingSuggestions suggestions = word.GetSpellingSuggestions("placa");

 

        foreach (SpellingSuggestion sg in suggestions)

        {

            Console.WriteLine(sg.Name);

        }

    }

 
מדהים איך שפיצ'ר אחד קטן גורם שהקוד שלנו נראה קריא ויפה יותר.
 
הנה הבקשה של GetSpellingSuggestion:
 
image2
 
 
 
בפוסט הבא (בעזרת ה' במוצאי שבת) נכנס אל מאחורי הקלעים של Optional Parameters ונבין איך זה עובד והאם יש לזה השפעה על הביצועים, ועוד.

C# 4.0 Part 8 - First glance on the Optional Parameters

 

אחד מהתכונות החדשות של C# 4.0 היא Optional Parameters.
 
למעשה - המצב עד היום שלא היה לנו את האופציה של פרמטרי רשות, הרגיל אותנו לכתוב קוד לא כל כך נכון, למה הכוונה - 
שימו לב לשני קטעי הקוד הבאים, הראשון - עושה overload למתודת Abs וקטע הקוד השני עושה overload למתודת Add. אז מה בכל זאת ההבדל ? 
 

    public static class Math

    {

        public static decimal Abs(decimal value);

        public static double Abs(double value);

        public static float Abs(float value);

        public static int Abs(int value);

        public static long Abs(long value);

        public static sbyte Abs(sbyte value);

        public static short Abs(short value);

    }

 

    public class Persons

    {

        public void Add(string name, int id, float age);

        public void Add(string name, int id);

        public void Add(string name);

    }

 
 
קטע הקוד הראשון באמת משתמש במנגנון ה - overload כמו שצריך, לכל מתודה יש מימוש שונה לפי סוג הפרמטר שהיא קבלה, לעומת זאת קטע הקוד השני משתמש במנגנון ה - overload כדי להקל על מי שישתמש ב - class הזה, והקוד היחיד שיש בתוך המתודות זה קריאה למתודה שמקבלת את כל המשתנים, ושליחת ערכי ברירת מחדל.
ומי שלא יצא לו לכתוב class שיש לו כמה Ctors שאחד קורה לשני שיקום.
 
סיבה נוספת להוספת תמיכה ב - Optional Parameters, היא תמיכה בקריאה לפונקציות משפות אחרות שתומכים ב - Optional Parameters, ונראה את זה בפוסט מחר.
 
 
אז איך משתמשים בתכונה הנפלאה הזאת.
 

    static void Add(int id, string name = "", float age = 0)

    {

    }

 
אנחנו כותבים בהתחלה את כל פרמרטרי החובה ואז את פרמטרי הרשות. אפשר להגדיר כערך ברירת מחדל רק מה שמוכר בזמן קומפילציה, ולכן נוכל לשלוח רק:
ערכי Const ל - Value Type או
null ל - Reference Type או
קריאה ל - default ctor של struct. לדוגמא - הההגדרה הבאה הינה חוקית:
 

   static void Add(int id = 0, string name = "",

                        Person person = null, DateTime dt = new DateTime())

   {

   }

 
לעומת זאת ניסיון לתת את DateTime.Now או את string.Empty כערך ברירת מחדל יגרום לשגיאת קומפילציה.
 
 
כשנקרא לפונקציה נראה את הדבר הבא:
 
 
Call function with optional parameters
 
 
יוצא מן הכלל האמור לעיל היא dynamic, לפרמטר מסוג dynamic אפשר לתת רק את הערך null היות שהקומפיילר חושב שזה  Reference Type, לדעתי זה באג ואני מקווה שזה יתוקן עד הגרסה הסופית, בכל מקרה מצאתי דרך לעקוף את הבעייה הזאת ולתת לפרמרטר מסוג dynamic כל ערך שנרצה, אבל זה יצטרך לחכות לפוסט של מחרתיים שבו אני אראה מה קורה מאחורי הקלעים של Optional Parameters ואז נבין איך אפשר לעקוף את הבעייה.
 
 
בפוסט מחר אני אדגים בעזרת ה' הפעלה של בדיקת איות של Word ותווכחו לדעת שעד ל - C# 4.0 הקוד היה מכוער מאוד והיום אפשר לכתוב את אותו קוד בצורה הרבה יותר הגיונית.

C# 4.0 Part 7 - dynamic Limitations

 

דברנו מספיק על dynamic, הגיע הזמן להתקדם לתכונות נוספות וחשובות של C# 4. הדבר היחיד שנשאר בנושא של dynamic זה לדעת מה אי אפשר לעשות באמצעות dynamic.
 
ההבנה הכי חשובה בנושא, היא - שאי אפשר (באמצעות CSharpBinder) לעשות עם dynamic יותר ממה שאפשר לעשות עם האובייקט במידה והיינו ניגשים אליו בצורה לא דינמית, היות שאנחנו יודעים שלמעשה dynamic הוא object. מה שכן - אפשר לרשת מ - DynamicObject או לרשת מ - CSharpBinder, (אבל לא כתבתי על זה עדיין פוסט).
 
 
1. אי אפשר לגשת למאפיין או מתודה שהם לא ברמת ההרשאות שלנו, כלומר לגשת ל - private או ל - internal מתוך assembly אחר.
 
2. אי אפשר לגשת למאפיינים או מתודות סטטיות מאחר שאי אפשר לגשת אליהם דרך האובייקט רק דרך הטיפוס. (הערת אגב - אני מקווה שב - C# 5.0 מייקרוסופט יוסיפו את האפשרות הזאת כמו שיש ב - VB.Net)
 
3. אי אפשר להפעיל Extension methods באמצעות dynamic.

C# 4.0 Part 6 - DynamicObject class and IDynamicMetaObjectProvider interface

 

אחד הדברים המתסכלים בללמוד חומר על טכנולוגיה חדשה שעדיין לא יצאה בגרסה סופית, זה שבכל גרסת ניסיון נוספת ה - API עלול להשתנות, וזה מתסכל כי אף אחד לא מעדכן אותך שה - API שהיה קיים בגרסה אחת השתנה והוא נראה שונה לגמרי בגרסה הנוכחית, ב - PDC הודגם ממשק שנקרא IDynamicObject - הממשק הזה נעלם, לא קיים יותר. ולמעשה היות שכל הפוסטים שלי בנושא אינם מדברים על הגרסה הסופית של המוצר, יכול מאוד להיות שכש - C# 4.0  תצא לאויר בגרסה רשמית הדברים שוב ישתנו. אז קחו את זה לתשומת לבכם.
 
 
 
כמו שהבטחתי בפוסט הקודם אני אראה בפוסט הנוכחי איך אפשר לשנות את ההתנהגות של dynamic object.
 
 
 
הדבר היחיד שאתם צריכים לעשות זה לרשת מ - class שנקרא DynamicObject
 
לדוגמא, אם אנחנו רוצים לכתוב אובייקט שמתנהג כמו Dictionary, רק שהגישה ל - keys תהיה גישה למאפיינים ולא דרך indexer, אפשר לכתוב את הקוד הבא:
 
 

    public class DynamicDictionary : DynamicObject

    {

        private Dictionary<string, object> dic = new Dictionary<string, object>();

 

        public override bool TrySetMember(SetMemberBinder binder, object value)

        {

            if (!dic.ContainsKey(binder.Name))

            {

                dic.Add(binder.Name, value);

            }

            else

            {

                dic[binder.Name] = value;

            }

 

            return true;

        }

 

        public override bool TryGetMember(GetMemberBinder binder, out object result)

        {

            if (!dic.ContainsKey(binder.Name))

            {

                return base.TryGetMember(binder, out result);

            }

 

            result = dic[binder.Name];

            return true;

        }

    }

 
והשימוש בו יראה כך:
 

    dynamic d = new DynamicDictionary();

    d.Name = "shlomo";

    d.Age = 24;

 

    Console.WriteLine(d.Name);

    Console.WriteLine(d.Age);

 
 
ולמעשה יצרנו אובייקט (קסום) שכאילו יוצר מאפיינים בלי שבאמת הם קיימים - כשהשמות של המאפיינים הם בפועל המפתחות ב - dictionary.
 
 
דוגמא נוספת, יותר שימושית היא - גישה לאלמנטים של xml באמצעות מאפיין ולא באמצעות indexer, פוסט ששווה לקרוא בנושא, כאן. (הדוגמא הבאה דומה לדוגמא בפוסט)
 
שימו לב לקוד:
 

    class DynamicXmlElement : DynamicObject

    {

        public XmlElement Element { get; private set; }

 

        public DynamicXmlElement(XmlElement element)

        {

            Element = element;

        }

 

        public override bool TryGetMember(GetMemberBinder binder, out object result)

        {

            List<XmlElement> elements = new List<XmlElement>();

            foreach (XmlElement item in Element)

            {

                if (item.Name == binder.Name)

                {

                    elements.Add(item);

                }

            }

 

            if (elements.Count == 0)

            {

                return base.TryGetMember(binder, out result);

            }

 

            if (elements.Count == 1)

            {

                result = new DynamicXmlElement(elements[0]);

                return true;

            }

 

 

            result = elements.ConvertAll<DynamicXmlElement>(e => new DynamicXmlElement(e));

            return true;

        }

 

        public override string ToString()

        {

            return Element.InnerText;

        }

    }

 
 
נעבור על הקוד:
 
ה - class יורש מ - DynamicObject ב - Ctor  הוא מקבל אובייקט מסוג XmlElement שהוא עובד איתו.
 
המתודה העיקרית TryGetMember מקבלת שני פרמטרים: הראשון - זה ה - binder שמכיל מידע אודות הבקשה והשני הוא result שאמור להחזיר את התשובה.
 
אני רץ על כל האלמנטים ב - Element בודק האם יש בתוכו אלמנטים עם השם שנשלח,
 
במידה ולא מצאתי שום אלמנט אני פונה ל - base שבסופו של דבר יחזיר Expression של זריקת שגיאה.
 
במידה ומצאתי רק אחד - אני מחזיר מופע חדש של DynamicXmlElement כשאני שולח את האלמנט שמצאתי ל - Ctor.
 
במידה ומצאתי יותר מאחד, אני מחזיר List של DynamicXmlElement.
 
 
נניח שה - Xml נראה כך:
 

<Persons>

  <Person>

    <Name>shlomo</Name>

    <Age>24</Age>

    <Childern>

      <Child>

        <Name>yossi</Name>

        <Age>6</Age>

      </Child>

      <Child>

        <Name>meir</Name>

        <Age>4</Age>

      </Child>

    </Childern>

  </Person>

  <Person>

    <Name>noam</Name>

    <Age>24</Age>

    <Childern>

      <Child>

        <Name>david</Name>

        <Age>3</Age>

      </Child>

      <Child>

        <Name>mika</Name>

        <Age>3</Age>

      </Child>

    </Childern>

  </Person>

</Persons>

 
 
נניח שיש לי מאפיין שנקרא SampleXml שמחזיר אותו, הקוד ב - Main יראה כך:
 

    XmlDocument doc = new XmlDocument();

    doc.LoadXml(SampleXml);

 

    dynamic dxe = new DynamicXmlElement(doc["Persons"]);

    dynamic persons = dxe.Person;

 

    foreach (dynamic item in persons)

    {

        Console.WriteLine(item.Name);

        Console.WriteLine(item.Age);

 

        foreach (dynamic child in item.Childern.Child)

        {

            Console.WriteLine(child.Name);

            Console.WriteLine(child.Age);

        }

    }

 
שימו לב שהגישה לכל האלמנטים של ה - xml היא דרך מאפיינים ולא דרך indexer או מתודות. שזה נראה הרבה יותר נחמד.
 
 
במידה ואתם לא רוצים לרשת מ - DynamicObject כי זה חוסם לכם את אפשרות הירושה ממישהו אחר אפשר לממש את IDynamicMetaObjectProvider
 
 

    public interface IDynamicMetaObjectProvider

    {

        DynamicMetaObject GetMetaObject(Expression parameter);

    }

 
והקוד שלכם נראה כך:
 

    class DynamicXmlDocument : XmlDocument, IDynamicMetaObjectProvider

    {

        #region IDynamicMetaObjectProvider Members

 

        public DynamicMetaObject GetMetaObject(Expression parameter)

        {

            return new XmlDocumentMetaObject(parameter, this);

        }

 

        class XmlDocumentMetaObject : DynamicMetaObject

        {

            public XmlDocumentMetaObject(Expression parameter, object value)

                : base(parameter, BindingRestrictions.Empty, value)

            {

 

            }

        }

        #endregion

    }

 
אבל אז צריך לעשות override למתודות של DynamicMetaObject וזה באמת נושא לפוסט נפרד.
Posted: Jun 23 2009, 08:31 AM by Shlomo | with 1 comment(s) |
תגים:, , , ,

C# 4.0 Part 5 - Behind The Scenes Part 2

 

אז אחרי שהבנו אתמול Expression Tree והבנו שלשום שלמעשה כל dynamic הוא object, כעת נראה מה קורה כשעושים פעולות על dynamic.
 
מה לדעתכם מתרנדר כשכותבים את הקוד הבא:
 

    int i = 10;

 

    dynamic d = i;

    d++;

 
למעשה מה שקורה זה הקוד הבא (בערך - כי שיניתי את השמות של המשתנים)
 

    private static class SiteContainer

    {

        public static CallSite<Func<CallSite, object, object>> p1;

    }

 

 

    static void Main(string[] args)

    {

        if (SiteContainer.p1 == null)

        {

            SiteContainer.p1 = CallSite<Func<CallSite, object, object>>.Create(

                new CSharpUnaryOperationBinder(

                    ExpressionType.Increment,

                    false,

                    new CSharpArgumentInfo[] {

                        new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));

        }

 

        d = SiteContainer.p1.Target.Invoke(SiteContainer.p1, d);

 

    }

 
טיפה מסובך, אבל לא נורא.
 
למעשה נוצר לנו class סטטי שמכיל אובייקט מסוג CallSite (שהוא בסך הכל אובייקט שאמור לקבל delegate) סוג ה - delegate שנוצר תלוי כמובן לפי הפעולה שאנחנו מנסים לעשות. כשמפעילים מתודות נוצר delegate (לפעמים)  מסוג Action.
 
ב - Main בודקים האם כבר יצרנו את אותו משתנה סטטי, במידה ולא, מייצרים אותו. וכאן יש המון עבודה של הקומפיילר שצריך לייצר את הקוד, ולמעשה כל האובייקטים שמסתיימים עם Binder למעשה בונים Expression שניתן לקמפל ולהריץ.
 
בשורה האחרונה מריצים את המתודה של ה - delegate והפעולה מתבצעת.
 
 
מה שחושב כאן להבין, שאין קסמים, כשעושים פעולות על dynamic למעשה מתרנדר קוד שבסופו של דבר יוצר Expression שמתאר את הקוד שאנחנו רוצים להריץ.
 
אפשר לחפור בנושא הזה הרבה יותר עמוק, אבל אני מרגיש שזה יפספס את המטרה של סדרת הפוסטים, שהיא - מעבר על התכונות החדשות של C# 4.0 והבנה איך זה עובד מאחורי הקלעים, כשאגמור עם סדרת הפוסטים אני אחזור לנושא הזה ונכנס הרבה יותר עמוק בכל מה שקשור ל - Binders למיניהם. (מה זה Binder מה הוא מכיל, ירושה והרחבה של הפונקציונליות, ועוד)
 
 
 
הפוסט הבא יראה איך אפשר להגדיר ל - DLR על אובייקט מסוים להתנהג בצורה אחרת כשמשתמשים ב - dymanic עליו, לדוגמא - עם יש לנו אובייקט שמכיל xml, נוכל לגשת על האלמנטים שלו כאילו הם מאפיינים ולא נצטרך לגשת דרך indexer. וזאת על ידי ירושה מ - DynamicObject.
 
ועל כך בפוסט הבא (מחר בעזרת ה')
Posted: Jun 22 2009, 07:41 AM by Shlomo | with 2 comment(s) |
תגים:, , ,

C# 4.0 Part 4 - Understanding Expression Tree

 

אז בפוסט הקודם ראינו שלמעשה אין ב - Runtime טיפוס dynamic, וכל מה שאנחנו משימים לתוך dynamic עובר להיות object.
 
בפוסט הזה הייתי רוצה להראות מה קורה מאחורי הקלעים כשעושים פעולות על אובייקט מסוג dynamic.
 
 
אבל - כדי להבין את הקוד שמתרנדר חייבים הבנה בסיסית ב - Expression Tree היות שכל המנגנון עובד עם Expression. ולכן בפוסט הזה אסביר על expression tree ובפוסט הבא נכנס למה שקורה מאחורי הקלעים.
 
 
כשהתחלתי ללמוד את C# 4.0 והבנתי שאני חייב להבין קודם Expression Tree, הגעתי לפוסט המצויין הזה, ואני ממליץ לכולם לקרוא אותו.
 
למעשה מייקרוסופט המציאו את ה - Expression Tree עבור Linq To Sql, כי הם חיפשו דרך נוחה לתאר קוד (כל ההסברים אודות LINQ נמצאים בפוסט שהפניתי לעיל)
 
 
נראה את הקוד הבא:
 

    Func<int, int, int> function = (int a, int b) => a + b;

או אם ננסה לפשט את זה -
 

    Func<int, int, int> function = execFunc;

 

    public static int execFunc(int a, int b)

    {

        return a + b;

    }

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

    ParameterExpression left = Expression.Parameter(typeof(int), "a");

    ParameterExpression right = Expression.Parameter(typeof(int), "b");

    BinaryExpression body = Expression.Add(left, right);

 

    LambdaExpression lamda = Expression.Lambda(typeof(Func<int, int, int>),

                                                body, left, right);

 

    Console.WriteLine(lamda.Compile().DynamicInvoke(2, 3));

 
השורה הראשונה - מייצרת ביטוי לפרמרטר בשם a מסוג int.
השורה השנייה - מייצרת ביטוי לפרמרטר בשם b מסוג int.
השורה השלישית - מייצרת ביטוי שמאחד את שני הפרמטרים כשאחד מצד ימין והשני מצד שמאל של הביטוי, והוא עצמו מתאר חיבור של שני הביטויים.
 
השורה הרביעית - מחזירה אובייקט מסוג LamdaExpression שמקבל כפרמטר טיפוס של delegate ואת הביטויים שמרכיבים אותו.
 
כעת קבלנו תיאור של קוד שמתאר קבלת שני מספרים חישוב סכומם והחזרת התוצאה.
 
במידה ואנחנו רוצים להריץ את הקוד, אנחנו צריכים להעביר אותו קומפליציה ולהפעיל את מתודה DynaimcInvoke.
 
 
למעשה את כל הקוד המסובך הזה אפשר להחליף אותו ב -
 

    Expression<Func<int, int, int>> expression = (a, b) => a + b;

    int res = expression.Compile()(2, 3);

 
הקוד הזה מייצר למעשה את כל הביטויים שיצרנו למעלה.
 
 
אחרי שהבנו קצת - מה זה Expression Tree, נוכל בפוסט הבא (שיהיה בעזרת ה' מחר) להיכנס באמת לנבכי הקוד שמתרנדר כשעושים פעולות על dynamic.

C# 4.0 Part 3 - Behind The Scenes Part 1

 

שני הפוסטים הקודמים עסקו בהבנה איזה שימוש אפשר לעשות עם ה - dynamic.
הראשון היה הדגמה פשוטה של היכולות בעוד השני הדגים את הדוגמא הפופולרית (שכולם כותבים אבל אף אחד לא מדגים) בכל המאמרים על C# 4.0 התוכנית GetCalculator.
 
 
כעת ננסה להבין מה מתרחש מאחורי הקלעים.
 
למעשה ההבנה מה מתרחש מאחורי הקלעים, מתחלקת לשניים
 
הראשון - מה קורה כשעושים השמה לתוך dynamic.
השני - מה קורה כשמפעילים מתודות או ניגשים למאפיינים של אותו dynamic.
 
הפוסט הזה יעסוק בשאלה הראשונה, הפוסט הבא יתחיל לענות על השאלה השנייה.
 
 
אז לפני הכל - השאלה שנשאלת, האם באמת קיים טיפוס חדש בשם dynamic ?
 
והתשובה היא לא, אין חיה כזאת ב - runtime שנקראת dynamic, מפתיע לא ?
 
שימו לב לקוד הבא:
 

    int i = 10;

    object o = new object();

 

    dynamic i1 = i;

    dynamic o1 = o;

 
 
שימו לב לקוד שמתרנדר מאחורי הקלעים:
 

    int i = 10;

    object o = new object();

 

    object i1 = i;

    object o1 = o;

 
לא מאמינים ? שימו לב לקוד ה - IL.
 
IL dynamic
 
 
יש כאן תהליך boxing רגיל שעובר על כל משתנה מסוג int כששמים אותו ל - object.
 
שימו לב לקוד הבא:
 

    static dynamic DynamicDemo(dynamic d1)

    {

        return null;

    }

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

    [return: Dynamic]

    private static object DynamicDemo([Dynamic] object d1)

    {

        return null;

    }

 
 
לסיכומו (החלקי) של עניין, אין טיפוס בשם dynamic, יש רק object, ו - attributes שאומר ל - CLR, שכאן צריך לחזור ולהתקבל אובייקט דינמי.
 
 
השאלה היותר מסקרנת, מה קורה כשמנסים להפעיל פעולות על ה - dynamic, וכמו שראינו זה בסך הכול object, ועל כך בפוסט הבא (שיהיה בעזרת ה' מחר.)
Posted: Jun 20 2009, 09:31 PM by Shlomo | with 2 comment(s)
תגים:, , ,

C# 4.0 Part 2 (The "GetCalculator program)

 

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

    object o = GetCalculator();

    MethodInfo mi = o.GetType().GetMethod("Add");

    int res = (int)mi.Invoke(o, new object[] { 2, 3 });

 
 
אפשר לכתוב את הקוד הבא:
 

    dynamic o = GetCalculator();

    int res = o.Add(2, 3);

 
 
הדוגמא הזאת תמיד הטרידה אותי,
הרי אי אפשר להפעיל מתודות שהם לא public באמצעות dynamic, אז בהכרח המתודות של ה - Calculator הם public, אז מדוע שמישהו ירצה להפעיל את מתודת Add ב - Reflection ?
גם אם אני לא מכיר את האובייקט האמיתי, אני יכול עדיין להשתמש ב - Interface.
 
חרשתי את הרשת בנסיון למצוא דוגמא אמיתית ל - GetCalculator, אבל לא מצאתי
ולכן כתבתי את הדוגמא הבאה - שמראה מימוש של מחשבון ב - net ומימוש ב - JS, והפעלת המתודות על ידי dynamic:
 
 
 
בשלב ראשון נבנה פרויקט בשם CalculatorImplementation שמכיל class בשם NetCalculator
 
 

    public class NetCalculator

    {

        public int Add(int a, int b)

        {

            return a + b;

        }

 

        public int Subtract(int a, int b)

        {

            return a - b;

        }

 

        public int Multiply(int a, int b)

        {

            return a * b;

        }

 

        public int Divide(int a, int b)

        {

            return a / b;

        }

    }

 
כעת נוסיף לפרויקט קובץ Htm בשם JSCalculator.htm
 

    <head>

        <script type="text/javascript">

 

            var Calculator = new Object();

            Calculator.Add = function(a, b) { return a + b; };

            Calculator.Subtract = function(a, b) { return a - b; };

            Calculator.Multiply = function(a, b) { return a * b; };

            Calculator.Divide = function(a, b) { return a / b; };

 

            function GetCalculator() {

                return Calculator;

            }

        </script>

    </head>

 
למעשה זה מימוש פשוט של אובייקט מחשבון ב - JS.
 
נסמן במאפיין Copy To Output Directory את האופצייה של Copy always (כדי שיהיה לנו את קובץ ה - htm אחרי קומפילציה) 
 
 
Copy to output
 
 
היות שאנחנו לא יכולים לגשת ישירות ל JS מתוך WinFormApplication אנחנו צריכים מישהו שיארח אותו, לכן נוסיף לפרויקט (שהוא מסוג ClassLibrary) קובץ מסוג WinForm שיש בו WebBrowser שה - URL שלו הוא לקובץ ה - htm שיצרנו, והוא למעשה יהיה ה - Host ל JS.
 
הקובץ נקרא JSHost
כאמור אין בו כלום מלבד WebBrowser - והוא נראה כך:
 
JSHost
 
הקוד היחיד שלו ב - Ctor נראה כך:
 

    public JSHost()

    {

        InitializeComponent();

 

        string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,

                                    "JSCalculator.htm");

 

        webBrowser1.Url = new Uri(path);

    }

 
הקוד טוען את ה - html שלנו לתוך ה - browser והוא יארח אותו ונוכל לגשת אליו (מה שהוא מחזיר לנו זה אובייקט Com)
 
 
כעת נוסיף לפרויקט class בשם CalculatorFactory, שיודע להחזיר לפי קונפיגורציה את המחשבון המתאים. (ההסברים מיד אחרי הקוד)
 

    public class CalculatorFactory

    {

        private JSHost m_JsHost;

        private object m_CalculatorObject;

        private static CalculatorFactory m_Instance;

 

        public static CalculatorFactory Instance

        {

            get

            {

                if (m_Instance == null)

                {

                    m_Instance = new CalculatorFactory();

                }

 

                return m_Instance;

            }

        }

 

        private CalculatorFactory()

        {

            m_JsHost = new JSHost();

            m_JsHost.Show();

            m_JsHost.Hide();

        }

 

        public object Calculator

        {

            get

            {

                if (m_CalculatorObject == null)

                {

                    if (CalculatorType == "Net")

                    {

                        m_CalculatorObject = new NetCalculator();

                    }

                    else if (CalculatorType == "JS")

                    {

                        m_CalculatorObject = m_JsHost.webBrowser1.Document.InvokeScript("GetCalculator");

                    }

                    else

                    {

                        m_CalculatorObject = null;

                    }

                }

 

                return m_CalculatorObject;

            }

        }

 

        public string CalculatorType

        {

            get

            {

                string type = ConfigurationManager.AppSettings["CalculatorType"];

                return type;

            }

        }

    }

 
אז ככה:
 
ה - class מכיל שלושה משתנים
הראשון הוא מופע של ה - JSHost,
השני הוא אובייקט המחשבון,
והשלישי הוא מופע של ה - Factory (מימוש פשוט של Singleton)
 
אחרי הגדרת המשתנים יש את המאפיין שמחזיר מופע בודד של ה - Factory.
 
ב - Ctor אני מייצר את ה - JSHsot, מפעיל את מתודת ה - Show ומיד את מתודת Hide, הסיבה לזה היא מאוד פשוטה, מכיון שכל עוד שה - Form לא יהיה במצב Show, ה - WebBrowser לא יארח את קובץ ה - htm, אבל מכיון שאני לא באמת רוצה לראות את ה - Form, אני מסתיר אותו.
 
המאפיין הבא מחזיר אם אובייקט המחשבון לפי מה שמוגדר בקובץ הקונפיגורציה, במידה ומוגדר Net אני מחזיר את ה - NetCalculator במידה ומוגדר JS אני מריץ את הסקריפט getCalculator שמחזיר את המחשבון מקובץ ה - JS (עטוף כאמור ב - COM)
 
והמאפיין האחרון מחזיר מה שמוגדר בקובץ הקונפיגורציה.
 
 
כעת נראה את השימוש בזה.
 
נוסיף פרויקט חדש מסוג WinFormApplication בשם CalculatorDemo,
 
הנה קובץ הקונפיגורציה:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <appSettings>

    <add key="CalculatorType" value="JS"/>

  </appSettings>

</configuration>

כעת נוסיף את קובץ ה - GUI הוא נראה כך:
 
 
Calculator Demo
 
אני מראה את הקוד של ה - Ctor והלחצן Add:
 
 
 

    public Form1()

    {

        InitializeComponent();

 

        this.Text = "Calculator Demo: " + CalculatorFactory.Instance.CalculatorType;

    }

 

    private void btnAdd_Click(object sender, EventArgs e)

    {

        Type calcType = CalculatorFactory.Instance.Calculator.GetType();

 

        int num1 = int.Parse(txtNum1.Text);

        int num2 = int.Parse(txtNum2.Text);

 

        int resault = Convert.ToInt32(

            calcType.InvokeMember("Add",

                                    BindingFlags.InvokeMethod,

                                    null,

                                    CalculatorFactory.Instance.Calculator,

                                    new object[] { num1, num2 }));

 

 

        txtResault.Text = resault.ToString();

    }

 
והנה אנחנו רואים קוד שניגש לאוביקט מה - Factory ומפעיל את המתודות שלו באמצעות Reflection , היות שאנחנו מקבלים מה - Factory אובייקט מסוג object שיכול להגיע מדוט נט או מ - COM, אנחנו חייבים להפעיל את זה באמצעות Reflection.
 
 
כעת נעשה שינוי אחד קטן ב - Factory, המאפיין שנקרא Calculator יחזיר dynamic במקום object, שימו לב:
 

    //public object Calculator

    public dynamic Calculator

 
זהו, זה השינוי היחיד, עכשיו שימו לב לקוד של הלחצן Add:
 

    private void btnAdd_Click(object sender, EventArgs e)

    {

        int num1 = int.Parse(txtNum1.Text);

        int num2 = int.Parse(txtNum2.Text);

 

        int resault = CalculatorFactory.Instance.Calculator.Add(num1, num2);

        txtResault.Text = resault.ToString();

    }

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

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.

Cancel post back from Radio button using javascript

 

נניח שיש לכם את קוד ה - html הבא:

    <asp:RadioButton ID="RadioButton1"

                    runat="server"

                    AutoPostBack="true"

                    OnCheckedChanged="RadioButton1_CheckedChanged" />

 

    <asp:RadioButton ID="RadioButton2"

                    runat="server"

                    AutoPostBack="true"

                    OnCheckedChanged="RadioButton2_CheckedChanged" />

 
מה שזה אומר - שיש לנו שני radio button שכל שינוי יעשה post back ויפעיל את המתודה הרלוונטית בצד השרת.
 
מה קורה אם אנחנו רוצים לפעמים לבטל את הריצה לשרת.
 
בהתחלה חשבתי שאני צריך להוסיף את הקוד הבא:
    

    onclick = "return CheckFunc()" />

בתוך אותו CheckFunc אפשר לעשות בדיקות ולהחזיר true או false, (בדיוק כמו שאנחנו עושים עם buttons)
 
אבל מסתבר שאם נכתוב קוד כזה, הריצה לשרת לא תתבצע אף פעם, והסיבה היא, שכך נראה קוד ה - html שמתרנדר

    <input id="Radio1"

            type="radio"

            name="RadioButton1"

            value="RadioButton1"

            onclick="return CheckFunc();setTimeout('__doPostBack(\'RadioButton1\',\'\')', 0)" />

 
מה שאומר שלא משנה מה שהפונקציה שלנו תחזיר, ה - dopostback לא יתבצע מכיון שאנחנו יוצאים מ - onclick.
 
במידה ורק נקרא ל - CheckFunc מבלי לכתוב return, ה - postback יתבצע תמיד.
 
 
הפיתרון הבא הוא טיפה מכוער, אבל עובד.
 
מה שאני עושה זה לקרוא לפונקציה (ולכן הריצה לשרת תתבטל) ובמידת הצורך מתוך הפונקציה לרוץ לשרת.
 

    function CheckFunc() {

        var res = confirm('Message ?');

 

        if (res) {

            setTimeout('__doPostBack(\'<%= r1.UniqueID %>\',\'\')', 0)

        }

    }

 
 
דרך אגב, למה הקריאה ל - __doPostBack נמצאת בתוך setTimeout ?

ג'יבריש בצד הלקוח (IE8 - IE6) - שימוש ב - unscape

 


מישהי בשם מיכל שלחה לי את השאלה הבאה:

יש לי דף ASP שיש בו את הקוד הבא:

alert ('<%=ErrMessage%>')
זה הופיע כג'יבריש על הדפדפן, ולכן הורדנו את התגית הבאה:
<META HTTP-EQUIV="Content-Type" content="text/html; charset=windows-1255">
 

וכן שמרנו את הדף ב - Unicode.

 

אבל אז זה הסתדר ב - IE8 אבל נהרס ב - IE6,

עד כאן השאלה,

לא ידעתי בדיוק למה זה קורה, אבל היה לי בעבר בעייה דומה, אז הצעתי לה להשתמש בפונקצית unescape, וזה עבד.

הקוד נראה כעת כך:

alert (unescape('<%=ErrMessage%>'))
 

זהירות עם הפעלת += על int? או חיבור שני int?

 

מה דעתכם על הקוד הבא :

    int? i = null;

    i += 10;

    Console.WriteLine(i);

 
אני הייתי בטוח שזה ידפיס 10, אבל מסתבר שהערך של i נשאר ב - null, וזה גם מתואר ב - MSDN
 

    int? d = null;

    int? r = 10;

 

    r = r + d;

    Console.WriteLine(r);

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

    if (i.HasValue)

    {

        i += 10;

    }

    else

    {

        i = 10;

    }

 
או :

    i = i.GetValueOrDefault() + 10;

 
או :

    i = i ?? 10;

Posted: Jun 12 2009, 08:00 AM by Shlomo | with 6 comment(s)
תגים:, ,

Override the parseInt method

פיני כתב שצריך להיזהר בעת שימוש ב parseInt
 
היות וברירת המחדל של הפונקציה - היא להשתמש בבסיס 8, ולכן הפעולה

    var a = parseInt("010")

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

    var originalParseInt = parseInt;

 

    parseInt = function(string, radix) {

        if (radix == null) {

            radix = 10;

        }

 

        return originalParseInt(string, radix);

    }

 
אני שומר בהתחלה reference לפונקציה המקורית,
 
לאחר מכן אני מגדיר מחדש את הפונקציה parseInt, ודואג לתת את הערך 10, במידה ולא שלחו בסיס אחר.
 
ולמעשה גרמתי לכך שכל מי שיקרא לפונקציה parseInt יגיע לפונקציה שאני כתבתי, יאתחל (אם צריך) את ה - radix ויפעיל את הפונקציה המקורית.
More Posts Next page »