Events, inheritance and polymorphism


بعد ما تخلص قراية هتعرف أكتر عن:
  1. Inheritance and overriding base class
  2. How to declare your own List class (with Add and Clear functions)
  3. Events and delegates
  4. Fire specific events when you add to/ remove from list (triggers): writing database logger.
المطلوب: لما يحصل تغيير في List سواء بالإضافة أو الحذف أو التعديل يتسجل نوع التغيير اللي حصل ووقته في ملف (للتبسيط هيكون string).
عشان تقدر تتحكم أكتر في العمليات اللي بتحصل في ال List هنكتب كلاس يـ inherit من كلاس ArrayList
(الأفضل إنك تحاول قدر الإمكان تبعد عن ال ArrayList عشان مش Type safe، وتـ  
Implement ICollection<T>
 بس هنستخدمها للتبسيط، المبدأ واحد في الحالتين)
نبدأ نـ Inherit من ArrayList ونـ override ال methods اللي محتاجينها (هنشتغل على Add و Clear)
عشان تـ fire event فدا معناه تهندله (handle) لما يحصل حدث معين، بتهندله في ميثود؛ الميثود دي شكلها ثابت في أي إيفنت، وهي إنها مش بترجع قيمة وبتاخد اتنين باراميترز، أول باراميتر الأوبجكت اللي أطلق الإيفنت، في المثال بتاعنا هو ال List لما يضاف ليها أو يتحذف منها. وتاني باراميتر معلومات إضافية عن الأوبجكت دا.
يبقا لما نـ Create event هنحتاج نديله الميثود دي، ولأننا مش عايزين حاجة من الميثود واحنا بن create الإيفنت يعني مش هنعملها run فاحنا بنديله فقط بوينتر للميثود مش أكتر، البوينتر دا اسمه ديليجيت، وهو اللي بيشيل ال signature بتاع الميثود اللي هتهندل الإيفنت، يعني ال return type وال parameters ، العرف المتبع إن الdelegate دي اسمها ينتهي ب EventHandler،
 قلنا إن أول باراميتر فيه مصدر الإيفنت (ودا عرفناه)، لكن تاني باراميتر فيه معلومات إضافية، طيب نعمله ازاي؟؟
البارميتر دا من نوع EventArgs أو أي class بيـ Inherit منه، كلاس عادي فيه الداتا اللي عايز تاخدها وانت بتهندل الإيفنت، في المثال بتاعنا DB state& DateTime TimeChanged:
public enum DBState
{
    Added, Cleared
}
public class DBLoggerEventArgs : EventArgs
{
    public DBState DbState { get; set; }
    public DateTime TimeNow { get; set; } = DateTime.Now;
}

 كدا خلصنا من تاني باراميتر في الميثود اللي هتهندل الإيفنت، طيب إيه هو أول باراميتر.. اللذيذ في أول باراميتر إنه دايما بيكون من نوع أوبجكت يقدر يشيل أي سورس، فاحنا كدا جاهزين إننا نعمل الdelegate اللي هتكون بوينتر للmethod اللي هتهندل الإيفنت، كالتالي:
public delegate void DBChangedEventHandler(object sender, DBLoggerEventArgs e);
وجاهزين نعمل الكلاس اللي بيImplement الArrayList
مبدأيًا هنـ override اتنين ميثود (Add, Clear) ولما يحصل تغيير فيهم هنـ fire الـ eventt، بعد طبعا ما نعرّف الـ event

public class LiveList : ArrayList
{
    public event DBChangedEventHandler Changed;
    protected virtual void OnChanged(DBLoggerEventArgs e)
    {
        Changed?.Invoke(this, e);
    }

    public override int Add(object value)
    {
        int i = base.Add(value);
        DBLoggerEventArgs e = new DBLoggerEventArgs();
        e.DbState = DBState.Added;
        OnChanged(e);
        return i;
    }
    public override void Clear()
    {
        base.Clear();
        DBLoggerEventArgs e = new DBLoggerEventArgs();
        e.DbState = DBState.Cleared;
        OnChanged(e);
    }
}

الكود دا:
Changed?.Invoke(this, e);

وظيفته إنه يتأكد إنه هيـ fire الـ event لما يكون فيه subscribers.
لو شفت ميثود ال Add كمثال، فانا بضيف عادي لل List وبعد كدا بـ Create أوبجكت الـ EventArgs-derived class واخزن جواه أي داتا إضافية، وبعدين بستدعي الميثود اللي بت raise ال event.
كدا الكلاس دا خلص.
دلوقي أبسط تعريف للإيفنت إنه نوتيفيكشن من مكان لمكان، بشكل أدق من sender لـ subscriber
مين اللي هي subscribe؟
اللي هيـ subscribe هو الـ logger اللي هيسجل الlogs بتاعة الداتا بيز:
فيه property بتاخد كل تفاصيل ال logs، يللا:
public class DBLog
{
    public string Log { get; set; } = string.Empty;
    public DBLog(LiveList nList)
    {
        nList.Changed += NList_Changed;
    }
    private void NList_Changed(object sender, DBLoggerEventArgs e)
    {
        Log = e.TimeNow + ": " + e.DbState + "\n";
        Console.WriteLine("DBLOG: " + Log);
    }
}
علامة += دي الل بتـ subscribe ال DBLog للإيفنت، و -= بت unsubscribe
 انا عملتها في الكونستراكتور عشان اقول للكلاس لما أنشيء منك أوبجكت، ركز مع أي تغيير يحصل في الداتابيز.
الطرف الأيمن دا بقا البوينتر للميثود اللي هتتنفذ لما ال event يحصل. ودي ميثود زي ما انت لسه فاكر (او ناسي) مش بترجع حاجة، وبتاخد باراميتر للسورس اللي عمل fire ال event وباراميتر فيه معلومات إضافية:
كدا كلاس ال subscriber خلص
تعالى نشغل البرنامج بقا.. في ال Main:
نـ create أوبجكت من الليست بتاعتنا:
LiveList someData = new LiveList();
نـ create أوبجكت من اللوجر:
DBLog logger = new DBLog(someData);
نجرب نشوف هل هو سامع التغييرات اللي بتحصل ولا لأ:
someData.Add("Hello");
Thread.Sleep(2000);//to simulate time span
someData.Clear();
 النتيجة:

DBLOG: 02-May-17 11:27:46 PM: Added
DBLOG: 02-May-17 11:27:48 PM: Cleared
سامع كل حاجة بتحصل، وبكدا نكون عملنا logger للي بيحصل في الداتابيز.. ممكن تاخد نفس الكونسبت، وتعمل تريجرز زي اللي في SQL لل entity framework

Comments

Popular posts from this blog

Creating RadioButtonsGroup control in Xamarin.Forms

إعادة اكتشاف الصفر والواحد