DCSIMG
C# 4.0 Part 2 (The "GetCalculator program) - שלמה גולדברג (הרב דוטנט)

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

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

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, ומה מתחולל מאחורי הקלעים.
פורסם: Jun 19 2009, 09:57 AM by Shlomo | with 4 comment(s)
תגים:, , , ,

תוכן התגובה

Shlomo כתב/ה:

זה לא בדיוק Reflection, בפוסטים הבאים אני אכנס למה שמתחולל מאחורי הקלעים

# June 20, 2009 10:04 PM

דני כתב/ה:

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

# July 4, 2009 1:05 PM

Shlomo כתב/ה:

את בלא יכול לצפות מאובייקט שנכתב ב JS לממש את הממשק, נכון ?

# July 4, 2009 10:44 PM
שלח תגובה

(שדה חובה)  

(שדה חובה)  

(אופציונלי)

(שדה חובה) 

Please add 5 and 4 and type the answer here:


Enter the numbers above: