C# 委托与事件

355 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

1、委托

委托是一种数据类型,像类一样,它定义了方法的类型,使得可以将定义的方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

委托保存的是对函数(function)的引用,即保存对存储在托管堆(managed heap)中的对象的引用,而不是实际值。

委托实例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DelegateProgram
{
    //可以声明在类外
    // public delegate void delegateTest(string msg);
    static class Program
    {
        //声明在类内部
        public delegate void delegateTest(string msg);

        public delegate string delegateTest2<T,E>(T t,E e);

        static void Main()
        {
            Function function = new Function();
            //代理 简单应用
            delegateTest simpleApplication = new delegateTest(function.FunctionA);
            simpleApplication("simple");
            //代理 组合应用
            delegateTest combinationA, combinationB, combinationC,combinationD;
            combinationA = new delegateTest(function.FunctionA);
            combinationB = new delegateTest(function.FunctionB);
            combinationC = combinationA + combinationB;
            combinationD = combinationC - combinationB;
            combinationA("A");
            combinationB("B");
            combinationC("C");
            combinationD("D");
            //代理 泛型应用
            delegateTest2<string, string> generic = new delegateTest2<string, string>(function.FucntionT);
            Console.WriteLine(generic("T","E"));
        }

    }

}

Function类 

public class Function 
    {
        public void FunctionA(string msg)
        {
            Console.WriteLine("FunctionA:{0}", msg);
        }

        public void FunctionB(string msg)
        {
            Console.WriteLine("FunctionB:{0}", msg);
        }

        public string FucntionT<T, E>(T t, E e) 
        {
           return(string.Format("泛型代理参数值:{0},{1}",t,e));
        }
    }

输出结果:

FunctionA:simple
FunctionA:A
FunctionB:B
FunctionA:C
FunctionB:C
FunctionA:D
泛型代理参数值:T,E

2、事件

事件:事件是对象发送的消息,发送信号通知客户发生了操作。这个操作可能是由鼠标单击引起的,也可能是由某些其他的程序逻辑触发的。事件的发送方不需要知道哪个对象或者方法接收它引发的事件,发送方只需知道它和接收方之间的中介(delegate)。

对于C#编程我们最常看到的一个事件就是窗体加载的时候触发的Load事件

namespace DelegateProgram
{
    public partial class EventTest : Form
    {
        public EventTest()
        {
            InitializeComponent();
        }

        private void EventTest_Load(object sender, EventArgs e)
        {
            MessageBox.Show("Load事件");
        }
    }
}

现在我们看到当窗体加载的时候就会触发这个时间。而我们也许不知道怎么触发的,现在我们手动让这个事件触发,还原C#中事件的一些本质。

        public EventTest()
        {
            InitializeComponent();
            this.Load+=myEventTest_Load;
        }

        private void myEventTest_Load(object sender, EventArgs e)
        {
            MessageBox.Show("手动添加Load事件");
        }

我们可以看到这样手动添加事件,而不是编译器的帮助。但是this也是.net为我们封装的,所以我们没有看到事件在哪里执行的代码。进一步改进:

        public event EventHandler load;
        
        public EventTest()
        {
            InitializeComponent();
            load+=myEventTest_Load;
            load(this, null);
        }

        private void myEventTest_Load(object sender, EventArgs e)
        {
            MessageBox.Show("手动添加触发Load事件");
        }

在上一步的改进中 EventHandler就是一个委托类型,从中我们可以看出事件是委托的最终表现形式,也就是说事件就是委托的一种高级用法,事件就是利用委托实现的。 当然我们还可以更彻底的还原,将EventHandler变成我们自己得委托。

        //定义自己的代理
        public delegate void MyEventHandler(string msg);

        public event MyEventHandler load;
        
        public EventTest()
        {
            InitializeComponent();
            load+=myEventTest_Load;
            load("自定义代理load事件");
        }

        private void myEventTest_Load(string msg)
        {
            MessageBox.Show(msg);
        }

当到这里对于代理和事件的理解也应该更明朗些了吧。事件是代理的应用,他们之间也存在一些差异委托是一个类,而事件是依赖于类的,所以委托可以声明在类的外面,而事件必须写在类的里面。 事件必须建立在委托的基础上才能得以实现,即先声明委托再定义事件。

事件是可连续触发的,所以对于事件的赋值要采用“+=”和“-=”运算,“+=”代表累积事件,“-=”代表移除某一事件。