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

委託

委託是什麼?

委託是一種引用類型(其實就是一個類,繼承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();
        }
    }
}

參考