יום שני, 27 בינואר 2014

C# Reflection Guide



אחד התחומים המעניינים בפיתוח, האפשרות לתוכנית ללמוד על עצמה תוך כדי ריצה מאפשרת לפתח תוכניות מיוחדות במיוחד, האמת שנושא ה Reflection ישן והוא קיים עוד מתחילת ה Framework הראשון אבל אף פעם לא התייחסתי אליו כי בדרך כלל אין בו צורך, לרוב אנחנו יודעים מה אנחנו הולכים לפתח אבל קיימים מקרים מסויימים בהם יש חור שחור ואין לנו מספיק מידע ולכן עלינו לשמור על קוד גנרי ככל האפשר.

Reflection

יכולת של תוכנית לזהות במהלך הריצה את האובייקטים שרצים בה, לנתח אותם ולהפעיל בהם פונקציות באופן דינמי לפי סוג האובייקט או על פי קונבנציה קבועה, במאמר זה אני יעבור על נושא ה Reflection בסביבת .Net אבל היכולת הזאת נתמכת גם בסביבות נוספות כמו JavaScript, C++,Phyton וכו', הרעיון המרכזי הוא להפוך את התוכנית ל"שקופה" ושתוכל להתמודד עם אובייקטים שונים מבלי לעבור Compile כל פעם מחדש.

לצורך הדוגמה בניתי תוכנית והיא מאפשרת למשתמשים ליצור עבורה Plugins מיוחדים, מהרגע שהכנסתי את התכונה הזאת אני לא יודע מה המשתמש יעשה (האמת שזה לא כלכך צריך לעניין אותי), ואני לא יכול לעשות Compile לתוכנית המקורית כל פעם מחדש כי זה יהיה מגוחך, כאן נכנס ה Reflection לתמונה שבעזרתו אני יכול להגדיר קונבנציה עבור טעינת Plugin כחלק מה SDK של התוכנית, משתמש הקצה מייצר אובייקט שמכיל בתוכו קריאות לפונקציות שנקבעו מראש שמטרתם לחבר בין התוכנית לקוד שהמשתמש יצר, ברגע שמופעלת הפונקציה הקוד של המשתמש רץ מבלי שהיה חלק מהתוכנית עצמה כפי שניתן לראות בקוד הבא:

public class example
    {
        //private string
        private string msg0 = "this is exmaple of private string!";

        //public string
        public string msg1 = "this is exmaple of public string!";

        //constructor
        public example()
        {

        }

        //public function for invoke return string
        public string returnPrivateMessage()
        {
            return msg0;
        }

        //public function for invoke getting external 
        //parameter and return string
        public string returnEditMessage(string msg)
        {
            return "you write: " + msg;
        }
    }


המשתמש עוטף את המחלקה כ DLL ומעתיק אותה לתיקיית Plugin בתוכנית,התוכנית טוענת אותו לזיכרון ומחפשת את הפונקציה שנקבעה מראש על מנת להפעיל אותה ובעצם מעבירה את הריצה ל DLL מבלי לדעת מה בדיוק עומד לקרות שם.


class Program
    {
        static void Main(string[] args)
        {
            //load assmbly dynamically
            Assembly assemblyInstance = Assembly.LoadFrom(@"C:\\RefDLL.dll");

            Type[] types = assemblyInstance.GetTypes();

            for (int i = 0; i < types.Length; i++)
            {
                //search for specific type
                if (types[i].Name.ToLower() == "example")
                {
                    MethodInfo[] methods = types[i].GetMethods();
                    if (methods != null)
                    {
                        for (int z = 0; z < methods.Length; z++)
                        {
                            //search for specific method
                            if (methods[z].Name == "returnPrivateMessage")
                            {
                                //create instance for dynamic object
                                object classInstance = Activator.CreateInstance(types[i], null);

                                //execute method and gettig string
                                object res = methods[z].Invoke(classInstance, new object[0]);

                            }

                            if (methods[z].Name == "returnEditMessage")
                            {
                                //create parameters array for function,
                                //you can get the types of the arguments using
                                //ParameterInfo[] parameters = methods[z].GetParameters();
                                object[] parametersArray = new object[] { "my information!" };
                                object classInstance = Activator.CreateInstance(types[i], null);

                                object res = methods[z].Invoke(classInstance, parametersArray);
                            }
                        }
                    }
                }
            }

        }
    }



ראינו כיצד ניתן להפוך את התוכנית למשהו גנרי אבל מצד שני הקוד שלנו חשוף וכל אחד יכול להפעיל אותו, הצגתי בעבר כיצד ניתן לעשות Disassembly לקבצי DLL שנוצרו ב .Net וה Reflection לוקח חלק חשוב בתהליך, לסיום החלטתי שזו הזדמנות טובה להראות את היכולות של ה Reflection בעזרת כלי קטן שמאפשר לנתח קבצי DLL בצורה נוחה ומהירה, בדומה לדוגמה הקודמת התוכנית מקבלת נתיב ל DLL ופורסת אותו, היא מראה את הפונקציות, המשתנים והאירועים של האובייקטים בקובץ.

DLLReflector


סיכום:

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

קוד להורדה:


סקיינט?



אין תגובות:

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