C#:委託和自定義事件

Rain Man發表於2014-04-07

1. 委託概述

“委託”相當於C++中的“函式指標”,委託必須與所要“指向”的函式在“引數”和“返回型別”上保持一致;

// 定義Person類
public class Person {
    public string Name = "Rain Man";
    public string Speak(string words) {
        Console.WriteLine(this.Name + " said: " + words);
        return words;
    }
}

// 定義委託
public delegate string Dele_Speak(string str);

class Program {
    static void Main(string[] args) {
        Person p = new Person();                    // 例項化Person類
        Dele_Speak dp = new Dele_Speak(p.Speak);    // 例項化委託:變數dp實際上就是指向p.Speak函式的指標
        dp("Welcome to my blog!");                  // 輸出:Rain Man said: Welcome to my blog!
        Console.ReadLine();
    }
}
  • 代理“Dele_Speak”與“Speak”方法在引數和返回型別保持一致;
  • “Dele_Speak dp = new Dele_Speak(p.Speak)”,實際上就是建立了一個“dp”指標,指向“p.Speak”方法
  • “dp("Welcome to my blog!")”,實際上就是“p.Speak("Welcome to my blog!")”

2. 多路廣播

// 定義Person類
public class Person {
    public string Speak(string words) {
        Console.WriteLine("Speak: " + words);
        return "111";
    }
    public string Say(string words) {
        Console.WriteLine("Say: " + words);
        return "222";
    }
    public string Translate(string words) {
        Console.WriteLine("Translate: " + words);
        return "333";
    }
}
// 宣告代理 public delegate string Dele_Str(string str); class Program { static void Main(string[] args) { Person p = new Person(); // 例項化Person類 Dele_Str dp_Speak = new Dele_Str(p.Speak); // 例項化委託指向 p.Speak Dele_Str dp_Say = new Dele_Str(p.Say); // 例項化委託指向 p.Say Dele_Str dp_Translate = new Dele_Str(p.Translate); // 例項化委託指向 p.Transpate // 多路廣播 dp_Speak = dp_Speak + dp_Say; dp_Speak = dp_Speak + dp_Translate; string str = dp_Speak("Rain Man"); Console.WriteLine(str); // 輸出:333 Console.ReadLine(); } }

在Person類中建立了三個函式:Speak、Say、Translate,這三個函式在引數和返回型別上相同,因此可是使用同一個委託(Dele_Str)。

多路委託:使用同一個委託“指向”不同的函式,使這幾個函式可以“計算”,其執行邏輯如下:

執行:
    string str = dp_Speak("Rain Man");
輸出:
    Speak: Rain Man
    Say: Rain Man
    Translate: Rain Man

實際上就是執行下述程式碼:
    p.Speak("Rain Man");
    p.Say("Rain Man");
    p.Translate("Rain Man");

返回值:即最後一個函式的返回值

3. 事件代理

有兩個窗體:

  • FrmMain:該窗體中有一個按鈕“btnAdd”,當點選此按鈕時通過ShowDialog()方法開啟“FrmUserAdd”窗體
  • FrmUserAdd: 該窗體中有一個按鈕“btnOK”,當點選此按鈕時“對外”(對FrmMain窗體)傳送一個“UserAddEvent”事件,通過該事件將“FrmUserAdd”中填寫的“使用者資訊”傳至“FrmMain”窗體中。

3.1 FrmUserAdd窗體:

public partial class FrmUserAdd : Form 
{ // 1. 定義事件引數類 public class UserAddEventArgs : EventArgs { public User AddedUser; public UserAddEventArgs(User user) { this.AddedUser = user; } } // 2. 定義委託,並指定引數型別 public delegate void UserAddEventHandler(object sender, UserAddEventArgs e); // 3. 定義事件,並指定該事件的委託型別 public event UserAddEventHandler UserAddEvent; private void btnOK_Click(object sender, EventArgs e) { User user = new User(1, "Rain Man", ""); UserAddEventArgs args = new UserAddEventArgs(user); if (UserAddEvent != null) { this.UserAddEvent(this, args); } } }

3.1.1. 自定義事件引數類:UserAddEventArgs

自定義的事件引數類“UserAddEventArgs”必須繼承自“EventArgs”類,在此基礎上新增了public成員“AddedUser”

3.1.2 定義委託:UserAddEventHandler

  • 注意該委託的引數型別,第二個引數為“自定義的事件引數”。
  • 該委託用於在“FrmMain”窗體中例項化,例項化後繫結事件處理函式“OnUserAdd”。

3.1.3 定義事件變數:UserAddEvent

“UserAddEvent”變數可以理解為“UserAddEventHandler”委託的一個例項化物件,即

public UserAddEventHandler UserAddEvent;    // 在該示例中把"event"修飾符去掉也是可以的

3.2 FrmMain窗體

public partial class FrmMain : Form {
    // UserAddEvent事件繫結的處理函式
    private void OnUserAdd(object sender, FrmUserAdd.UserAddEventArgs e) {
        MessageBox.Show(e.AddedUser.username);
    }

    private void btnAdd_Click(object sender, EventArgs e) {
        FrmUserAdd frm = new FrmUserAdd();
        FrmUserAdd.UserAddEventHandler dele_fn = new FrmUserAdd.UserAddEventHandler(OnUserAdd);

        frm.UserAddEvent += dele_fn;
        frm.ShowDialog();
    }
}

3.2.1 FrmUserAdd.UserAddEventHandler dele_fn = new FrmUserAdd.UserAddEventHandler(OnUserAdd);

dele_fn為“UserAddEventHandler”的一個例項(指標),它指向事件處理函式“OnUserAdd”

3.2.2 frm.UserAddEvent += dele_fn;

可以看出此處實際就是“多路廣播”,同時也可以看出“UserAddEvent”事件變數實際就是“UserAddEventHandler”委託的一個例項。

3.3 執行邏輯

該示例看似複雜,其實質是將本在“一個窗體”中的實現,拆成了“兩個窗體”。下面將兩個窗體的程式碼合成“一個窗體”

public partial class FrmUserAdd : Form {
    // 定義事件引數
    public class UserAddEventArgs : EventArgs {
        public User AddedUser;
        public UserAddEventArgs(User user) {
            this.AddedUser = user;
        }
    }

    // 定義委託,並指定引數型別
    public delegate void UserAddEventHandler(object sender, UserAddEventArgs e);

    // 定義事件,並指定該事件的“委託”
    public UserAddEventHandler UserAddEvent;
    public event UserAddEventHandler UserAddEvent;
    
    // UserAddEvent事件繫結的處理函式
    private void OnUserAdd(object sender, FrmUserAdd.UserAddEventArgs e) {
        MessageBox.Show(e.AddedUser.username);
    }

    private void btnOK_Click(object sender, EventArgs e) {
        User user = new User(1, "Rain Man", "");
        UserAddEventArgs args = new UserAddEventArgs(user);

        FrmUserAdd.UserAddEventHandler dele_fn = new FrmUserAdd.UserAddEventHandler(OnUserAdd);
        this.UserAddEvent += dele_fn;

        if (UserAddEvent != null)
        {
            this.UserAddEvent(this, args);
        }
    }
}

相關文章