.NET委託,事件和Lambda表示式

深度碼農發表於2020-09-12

委託

委託是什麼?

委託是一種引用型別(其實就是一個類,繼承MulticastDelegate特殊的類。),表示對具有特定引數列表和返回型別的方法的引用。

每個委託提供Invoke方法, BeginInvoke和EndInvoke非同步方法

為什麼需要委託?

  • 委託可以將方法(即邏輯)作為引數;
    • 邏輯解耦,保持穩定。
    • 程式碼複用,保證專案規範。

如何使用委託?

如何宣告、例項化和使用委託

宣告委託

delegate void Del(string str);
static void Notify(string name)
{
    Console.WriteLine($"Notification received for: {name}");
}

例項化委託

Del del1 = new Del(Notify);
//C# 2.0
Del del2 = Notify;

呼叫委託

del1.Invoke("小明");
del2("小明");

其他使用委託

//C# 2.0使用匿名方法來宣告和例項化委託
Del del3 = delegate(string name)
{ Console.WriteLine($"Notification received for: {name}"); };
//C# 3.0使用lambda表示式宣告和例項化委託
Del del4 = name =>  { Console.WriteLine($"Notification received for: {name}"); };

簡化開發過程,.NET 包含一組委託型別:

  • Action<> 具有引數且不返回值。
  • Func<> 具有引數且返回由引數指定的型別的值。
  • Predicate<> 用於確定引數是否滿足委託條件的情況。

實際案例

程式碼:

class Program
{
    /// <summary>
    /// 宣告委託
    /// </summary>
    /// <param name="fullName"></param>
    private delegate void KillDelegate(string fullName);
    static void Main(string[] args)
    {
        //例項化委託
        var killWithKnifeDelegate = new KillDelegate(KillWithKnife);
        Kill("郭靖", killWithKnifeDelegate);

        var killWithSwordDelegate = new KillDelegate(KillWithSword);
        Kill("黃蓉", killWithSwordDelegate);

        var killWithAxeDelegate = new KillDelegate(KillWithAxe);
        Kill("歐陽克", killWithAxeDelegate);

        Console.ReadKey();
    }

    static void Kill(string fullName, KillDelegate killDelegate)
    {
        Console.WriteLine($"{fullName}遇到怪物");
        //呼叫委託
        killDelegate.Invoke(fullName);
        Console.WriteLine($"{fullName}增長10經驗");
    }

    static void KillWithKnife(string fullName)
    {
        Console.WriteLine($"{fullName}用刀殺怪物");
    }
    static void KillWithSword(string fullName)
    {
        Console.WriteLine($"{fullName}用劍殺怪物");
    }
    static void KillWithAxe(string fullName)
    {
        Console.WriteLine($"{fullName}用斧殺怪物");
    }
}

Lambda表示式

Lambda是什麼?

Lambda就是使用委託的更方便的語法。

//C# 2.0使用匿名方法來宣告和例項化委託
Del del3 = delegate(string name)
{ Console.WriteLine($"Notification received for: {name}"); };
//C# 3.0使用lambda表示式宣告和例項化委託
Del del4 = name =>  { Console.WriteLine($"Notification received for: {name}"); };

為什麼需要Lambda?

簡化開發過程,並不會影響執行效能。

如何使用Lambda?

表示式lambda基本形式:

//僅當 lambda 只有一個輸入引數時,括號才是可選的;否則括號是必需的
(input-parameters) => expression

使用空括號指定零個輸入引數:

Action line = () => Console.WriteLine();

括號內的兩個或更多輸入引數使用逗號加以分隔:

Func<int, int, bool> testForEquality = (x, y) => x == y;

語句lambda

(input-parameters) => { <sequence-of-statements> }

語句 lambda 的主體可以包含任意數量的語句;

Action<string> greet = name =>
{
    string greeting = $"Hello {name}!";
    Console.WriteLine(greeting);
};
greet("World");
// Output:
// Hello World!

使用匿名委託和lambda程式碼:

public static void Main(string[] args)
{
    List<int> list = new List<int>();
    for (int i = 1; i <= 100; i++)
    {
        list.Add(i);
    }

    //使用匿名委託
    List<int> result = list.FindAll(
      delegate (int no)
      {
          return (no % 2 == 0);
      }
    );
    foreach (var item in result)
    {
        Console.WriteLine(item);
    }
    
    //使用Lambda
    List<int> result = list.FindAll(i => i % 2 == 0);
    foreach (var item in result)
    {
        Console.WriteLine(item);
    }
}    

事件

事件是什麼?

事件是一種特殊的委託型別,主要用於訊息或通知的傳遞。事件只能從事件的釋出型別中呼叫,並且通常基於EventHandler委託,該委託具有代表事件傳送者的物件和System.EventArgs派生的類,其中包含有關事件的資料。

何時使用委託和事件?

  • 偵聽事件是可選的:如果你的程式碼必須呼叫由訂閱伺服器提供的程式碼,則應使用基於委託的設計。如果你的程式碼在不呼叫任何訂閱伺服器的情況下可完成其所有工作,則應使用基於事件的設計。
  • 返回值需要委託:用於事件的委託均具有無效的返回型別,事件處理程式通過修改事件引數物件的屬性將資訊傳回到事件源。
  • 事件具有專用呼叫:包含事件的類以外的類只能新增和刪除事件偵聽器;只有包含事件的類才能呼叫事件。

如何使用事件?

釋出事件

定義事件資料

public class CustomEventArgs : EventArgs
{
    public CustomEventArgs(string message)
    {
        Message = message;
    }

    public string Message { get; set; }
}

宣告發布類中的事件

public delegate void CustomEventHandler(object sender, CustomEventArgs args);
public event CustomEventHandler RaiseCustomEvent;

//使用泛型版本
public event EventHandler<CustomEventArgs> RaiseCustomEvent;

訂閱事件

定義一個事件處理程式方法

void HandleCustomEvent(object sender, CustomEventArgs a)  
{  
   // Do something useful here.  
} 

使用(+=) 新增訂閱事件

publisher.RaiseCustomEvent += HandleCustomEvent;  

使用(-=) 取消訂閱事件

publisher.RaiseCustomEvent -= HandleCustomEvent;  

示例

using System;
namespace DotNetEvents
{
    // 定義事件資訊的類
    public class CustomEventArgs : EventArgs
    {
        public CustomEventArgs(string message)
        {
            Message = message;
        }
        public string Message { get; set; }
    }
    // 釋出事件的類
    class Publisher
    {
        // 使用EventHandler <T>宣告事件
        public event EventHandler<CustomEventArgs> RaiseCustomEvent;
        public void DoSomething()
        {
            RaiseCustomEvent(new CustomEventArgs("Event triggered"));
        }
    }
    //訂閱事件的類
    class Subscriber
    {
        private readonly string _id;
        public Subscriber(string id, Publisher pub)
        {
            _id = id;
            // 新增訂閱事件
            pub.RaiseCustomEvent += HandleCustomEvent;
        }
        // 定義一個事件處理程式方法。
        void HandleCustomEvent(object sender, CustomEventArgs e)
        {
            Console.WriteLine($"{_id} received this message: {e.Message}");
        }
    }
    class Program
    {
        static void Main()
        {
            var pub = new Publisher();
            var sub1 = new Subscriber("sub1", pub);
            var sub2 = new Subscriber("sub2", pub);
            // 呼叫引發事件的方法
            pub.DoSomething();
            Console.ReadKey();
        }
    }
}

參考

相關文章