2017年8月9日 星期三

[C#] Delegates and Events

  • 從 Lambda 使用至今,所使用的範圍越來越廣,而 Lambda 原型為 Delegate,因此需要詳細的了解此原理:Lambda 運算式 (C# 程式設計手冊)
  • Delegate 
    • 使用 Delegate 關鍵字
    • Delegate 為型別,可以放在 namespace
    • 宣告 Delegate 步驟 
      1. Declare delegate & Declare function to delegate 
        • 宣告的"函數回傳類型"與"引數的數目與類型"需完全相同
      2. Create delegate reference
      3. Point the reference to the add method
      4. Invoke the delegate
    • Code
    /// 
    /// (1A) Delcare delegate
    /// 每個 delegate 型別"封裝"
    /// I. 函數回傳類型
    /// II.引數的數目與類型
    /// 
    public delegate string ReturnMessage(string message);

    /// 
    /// Using Delegate
    /// 
    public class DelegateLib
    {
        /// 
        /// (2) Create delegate reference
        /// 
        public ReturnMessage returnMessage = null;

        /// 
        /// Demo the function.
        /// 
        public void DemoFunction()
        {
            // (4) Invoke the method through delegate object
            returnMessage("DelegateLib:Lyndon"); // 相同 returnMessage.invoke("Lyndon");
        }
    }
    • Using Delegate Code
 
        /// 
        /// (1B) Delcare function to delegate
        /// 定義符合 Delegate 型別的方法
        /// 需相同於
        /// I. 函數回傳類型
        /// II.引數的數目與類型
        /// 
        static string ShowMessage(string message)
        {
            Console.WriteLine(string.Format("Program:{0}", message));
            // return message
            return message;
        }

        static void Main(string[] args)
        {
            DelegateLib demoDelegate = new DelegateLib();
            // (3) Point the reference to the add method
            // delegate 支援多點傳送,因繼承System.MulticastDelegate類別
            // = : 將方法指定至 delegate
            // += : 將多個方法綁定到同個 delegate
            //      demoDelegate.returnMessage += ShowMessage;
            // -= : 將某個 delegate 參考從 delegate 清單中移除
            //      demoDelegate.returnMessage -= ShowMessage;
            // null : 移除所有委派
            //      demoDelegate.returnMessage = null;
            //      [Design Pattern - Observer]
            //      應避免此情況:
            //      對於客戶端,應該透過註冊/取消來操作自己對應事件,不應該任意進行賦值操作
            //      不小心將其他發佈者 delegate 類別直接賦值 null,將會取消掉發佈者所有的通知列表
            //      發佈事件通知應該由發佈者來觸發,不應該讓客戶端可以呼叫
            demoDelegate.returnMessage = ShowMessage; // 不強制 += / -=
            demoDelegate.DemoFunction();
        }
    • 沒有使用 event 的情況下,可以針對 delegate 進行任何的操作,因此風險很高
  • Delegate and Event 
    • Code 將 (2) Delcare event 進行改變
    

    /// 
    /// (1A) Delcare delegate
    /// 每個 delegate 型別"封裝"
    /// I. 函數回傳類型
    /// II.引數的數目與類型
    /// 
    public delegate string ReturnMessage(string message);

    public class DelegateEventLib
    {

        /// 
        /// (2) Delcare event 
        /// 避免使用 delegate 可能會破壞物件封裝性的問題
        /// I. delegate 可隨意進行賦值操作,若不小心使用者將會移除所有事件
        ///     所以利用 event 封裝 delegate 類型的變數,因此類別的外部,使用者透過 "+=" 和 "-=" 進行事件操作
        /// II.delegate 可以綁定特定的方法,宣告必須定義 public
        ///     但也破壞掉物件的封裝性特性,因為類別的內外都可以呼叫 delegate
        /// 
        public event ReturnMessage returnMessage = null;

        /// 
        /// Demo the function.
        /// 
        public void DemoFunction()
        {
            // (4) Invoke the method through delegate object
            returnMessage("DelegateEventLib:Lyndon"); // 相同 returnMessage.invoke("Lyndon");
        }
    }
  • Using Delegate and Event 
        /// 
        /// (1B) Delcare function to delegate
        /// 定義符合 Delegate 型別的方法
        /// 需相同於
        /// I. 函數回傳類型
        /// II.引數的數目與類型
        /// 
        /// The message.
        /// string
        static string ShowMessage(string message)
        {
            Console.WriteLine(string.Format("Program:{0}", message));
            // return message
            return message;
        }

        static void Main(string[] args)
        {
            DelegateEventLib demoDelegateEvent = new DelegateEventLib();
            // (3) Point the reference to the add method
            // delegate 支援多點傳送,因繼承System.MulticastDelegate類別
            // += : 將多個方法綁定到同個 delegate
            //      demoDelegate.returnMessage += ShowMessage;
            // -= : 將某個 delegate 參考從 delegate 清單中移除
            //      demoDelegate.returnMessage -= ShowMessage;
            demoDelegateEvent.returnMessage += ShowMessage; // 強制 += / -=
            demoDelegateEvent.DemoFunction();
        }

  • Func and Action (C# 2.0)
    • 內建提供兩種不同的委派
    • Func<T, TResult> 委派
      • 提供回傳值方法
      • in T: 這個委派所封裝之方法的參數類型
      • out TResult: 這個委派所封裝之方法的傳回值之類型
      • Code:將 DelegateEventLib 程式碼,進行修改,行為相完全相同

    public class DelegateEventLib
    {
        /// 
        /// (2) Delcare event 
        /// 避免使用 delegate 可能會破壞物件封裝性的問題
        /// I. delegate 可隨意進行賦值操作,若不小心使用者將會移除所有事件
        ///     所以利用 event 封裝 delegate 類型的變數,因此類別的外部,使用者透過 "+=" 和 "-=" 進行事件操作
        /// II.delegate 可以綁定特定的方法,宣告必須定義 public
        ///     但也破壞掉物件的封裝性特性,因為類別的內外都可以呼叫 delegate
        /// 
        public event Func< string, string> returnMessage = null;

        /// 
        /// Demo the function.
        /// 
        public void DemoFunction()
        {
            // (4) Invoke the method through delegate object
            returnMessage("DelegateEventLib:Lyndon"); // 相同 returnMessage.invoke("Lyndon");
        }
    }
    • Action<T> 委派
      • 未提供回傳值方法
      • in T: 這個委派所封裝之方法的參數類型
      • 如果上列宣告 string ShowMessage(string message) 為 void ShowMessage(string message)沒有回傳值,將可使用 Action 委派 
      • Code
    public class DelegateEventLib
    {
        /// 
        /// (2) Delcare event 
        /// 避免使用 delegate 可能會破壞物件封裝性的問題
        /// I. delegate 可隨意進行賦值操作,若不小心使用者將會移除所有事件
        ///     所以利用 event 封裝 delegate 類型的變數,因此類別的外部,使用者透過 "+=" 和 "-=" 進行事件操作
        /// II.delegate 可以綁定特定的方法,宣告必須定義 public
        ///     但也破壞掉物件的封裝性特性,因為類別的內外都可以呼叫 delegate
        /// 
        public event Action< string> returnMessage = null;

        /// 
        /// Demo the function.
        /// 
        public void DemoFunction()
        {
            // (4) Invoke the method through delegate object
            returnMessage("DelegateEventLib:Lyndon"); // 相同 returnMessage.invoke("Lyndon");
        }
    }

沒有留言:

張貼留言