C Sharp/Delegates and Events

維基教科書,自由的教學讀本

Template:C sharp/Navigation

介紹[編輯]

委託(delegate)與事件(event)是Windows或Web應用程式的基礎。委託基本上相當於C語言的函數指針;委託數據告訴C#一個事件被觸發時哪些方法被調用。事件是.NET framework通知某個動作發生了;事件包含了特定信息,如滑鼠點擊事件包含了哪個滑鼠按鍵在窗體的哪裡被點擊。

委託[編輯]

委託是一個結構,可引用一個方法,該方法以後可被調用。一個委託的聲明指示特定的方法簽名。一個或更多個方法的引用可以增添到一個委託實例上。委託實例可以被調用,實際上是該委託實例引用的所有方法按照加入該委託的次序被調用。一個簡單例子:

using System;
delegate void Procedure();

class DelegateDemo
{
    public static void Method1()
    {
        Console.WriteLine("Method 1");
    }

    public static void Method2()
    {
        Console.WriteLine("Method 2");
    }

    public void Method3()
    {
        Console.WriteLine("Method 3");
    }

    static void Main()
    {
        Procedure someProcs = null;

        someProcs += new Procedure(DelegateDemo.Method1);
        someProcs += new Procedure(Method2);  // Example with omitted class name

        DelegateDemo demo = new DelegateDemo();

        someProcs += new Procedure(demo.Method3);
        someProcs();
    }
}

上例中,delegate void Procedure();是一個委託的聲明,該語句是完全抽象,不會產生可執行代碼,僅僅是聲明Procedure為一個無參、返回為空的委託類型。

已被委託引用的方法可以刪除引用:

someProcs -= new Procedure(DelegateDemo.Method1);

從C# 2.0,增加或刪除方法引用可用更簡明的語法:

someProcs += DelegateDemo.Method1;
someProcs -= DelegateDemo.Method1;

調用一個沒有引用任何方法的委託,導致NullReferenceException。

注意,如果委託返回特定類型的結果,那麼調用一個引用了多個方法的委託,實際返回的是最後被執行的方法的結果。

匿名委託[編輯]

匿名委託是使用delegate關鍵字寫出委託的代碼,可以捕獲局部變量,被編譯器自動轉化為方法。例如:

using System;
delegate void Procedure();

class DelegateDemo2
{
    static Procedure someProcs = null;

    private static void AddProc()
    {
        int variable = 100;
 
        someProcs += new Procedure(delegate
            {
                Console.WriteLine(variable);
            });
    }

    static void Main()
    {
        someProcs += new Procedure(delegate { Console.WriteLine("test"); });
        AddProc();
        someProcs();
        Console.ReadKey();
    }
}

可以如普通方法一樣接受參數:

using System;
delegate void Procedure(string text);

class DelegateDemo3
{
    static Procedure someProcs = null;
    
    private static void AddProc()
    {
        int variable = 100;
 
        someProcs += new Procedure(delegate(string text)
            {
                Console.WriteLine(text + ", " + variable.ToString());
            });
    }
    
    static void Main()
    {
        someProcs += new Procedure(delegate(string text) { Console.WriteLine(text); });
        AddProc();
        someProcs("testing");
        Console.ReadKey();
    }
}

輸出為:

testing
testing, 100

Lambda表達式[編輯]

Lambda表達式是比匿名委託更為清晰的方式。語法為:

(type1 arg1, type2 arg2, ...) => expression

這等價於:

delegate(type1 arg1, type2 arg2, ...)
{
    return expression;
}

如果只有一個參數,則圓括號可省略。參數類型也可以省略,讓編譯去去推導。例如:

Func<string, int> myFunc = str => int.Parse(str);

這等效於:

Func<string, int> myFunc = delegate(string str)
{
    return int.Parse(str);
};

事件[編輯]

事件是一種特殊的委託,用於事件驅動編程。事件可以是類從成員,但即使其訪問指示符是public,事件只能在包含它的類中被調用,但在其它類可以為事件增加或減少方法引用。例如:

public delegate void ButtonClickedHandler();
class Button
{
    public event ButtonClickedHandler ButtonClicked;
    ButtonClicked += ()=>{Console.WriteLine("click simulation !")};    
    public void SimulateClick()
    {
        if (ButtonClicked != null)
        {
            ButtonClicked();
        }
    }
    ...
}

在其它類中的方法可以通過向該事件委託增加方法,來訂閱到該事件:

Button b = new Button();
b.ButtonClicked += ButtonClickHandler;

private void ButtonClickHandler()
{
    //Handle the event
}

即使這個事件被聲明為public,它之惡能在包含它的類中被直接fire。