C#中委托(delegate)与事件(event)的快速理解

个人觉得学习C#无比快活,简单易上手,很容易写出自己想要的程序。尤其是有其他面向对象语言基础的人更是能很快掌握C#基础,码出自己想要的代码。但是其他编程语言没有委托与事件,虽然不影响新手使用,但是自己实现类似功能要多额外的工作量。可能很多新手入门C#,如果没有面向对象基础,第一座大山就是面向对象,第二座大山就是委托与事件。有时间再写面向对象,尤记当年入门面向对象的痛苦。本文章目的是让不理解委托与事件的初学者快速上手,个人也是初学者,有错误欢迎指正。

关于委托(delegate)

有人说委托是函数指针,对于没有C++基础的人很难理解什么函数指针。在我看来委托是一类相同函数(返回值类型与参数列表一致)的链表。什么是返回值类型与参数列表一致的函数?这个很容易理解。如以下代码所示:

void showName(string name)
{

}
void showTitle(string title)
{

}

或者如下:

int plus(int x,int y){
    return x + y;
    }
int mult(int x, int y) {
    return x * y;
}

上面说了委托是链表。然后你在声明委托的时候,要说明链表中函数的返回类型与参数列表,比如我声明返回类型为void,参数列表为string的委托。

delegate void func(string f);
//我声明的func与下面两个函数返回类型与参数列表一致
 void showName(string name)
 {

 }
 void showTitle(string title)
 {

 }

 delegate int func2(int x, int y);
 //我声明的func2与下面两个函数返回类型与参数列表一致
 int plus(int x,int y){
 	return x + y;
 }
 int mult(int x, int y) {
     return x * y;
 }

上面提到很多“委托”这个词,其实后面还有省略的两个字“类型”。你声明委托是声明了一种类型。声明以后就可以把该类型当参数传入

delegate void func();
void showName(func name)
{

}

以上func name并没有报错因为声明委托结束,代码中就有了func这个类型。既然有类型,那么我们就可以根据类型生成实例。比如

delegate void func();
func l;

我们定义了一个返回值为void,无参数的func类型的链表。然后声明了一个具体实例l。这里用l就是list的意思,帮助大家容易理解。然后就可以往链表里添加函数。使用符号“+=”

delegate void func(string f);
static func l;
static void showName(string name)
{

}
static void showTitle(string title)
{

}

 static void Main(string[] args)
{
 //这里两个函数要加static(静态的)不然需要你通过具体类来引用,委托同理。

    l += showName;
    l += showTitle;
}

通过以上操作,你就在函数链表l添加了showName与showTitle两个函数。这里很像长鞭炮,你把函数串联起来了。如果你调用l这个委托,就相当于点燃了引线,所有函数就会依次运行一遍。

delegate void func(string f);
static func l;
static void showName(string name)
{
    Console.WriteLine("你先输出了名字,参数:"+name);
}
static void showTitle(string title)
{
    Console.WriteLine("你又输出了主题,参数:"+title);
}

 static void Main(string[] args)
{
    l += showName;
    l += showTitle;
    l("一个参数");
    Console.ReadKey();
}

运行结果:
在这里插入图片描述
当然你可以传入函数,在调用函数,效果一样。

delegate void func(string f);
static func l;
static void showName(string name)
{
    Console.WriteLine("你先输出了名字,参数:"+name);
}
static void showTitle(string title)
{
    Console.WriteLine("你又输出了主题,参数:"+title);
}
static void run(func l) { 
    l("一个参数");
}
 static void Main(string[] args)
{
    l += showName;
    l += showTitle;
    run(l);
    Console.ReadKey();
}

链表模型(l的模型)
在这里插入图片描述
如果使用=号将清空链表,使链表中只剩最后一个等号后的函数。也可以使用-=,来清除对应的函数。这里请大家自行测试。
具有返回值的委托类型,返回值默认是最后一个函数的返回值,就是最后添加的返回值。

 delegate int func(int j,int k);
 static func l;
 static int plus(int x,int y){
     return x + y;
     }
 static int mult(int x, int y) {
     return x * y;
 }
  static void Main(string[] args)
 {
     l += plus;
     l += mult;
     Console.WriteLine(l(4,5));
     Console.ReadKey();
 }

运行以上返回值是20,结果是最后一个函数返回值。如何获取每个函数的返回值,这里写了一个示例:

delegate int func(int j,int k);
static func l;
static int plus(int x,int y){
    return x + y;
    }
static int mult(int x, int y) {
    return x * y;
}
 static void Main(string[] args)
{
    l += plus;
    l += mult;
    foreach(Delegate i in l.GetInvocationList()){
       func k=(func)i;
       Console.WriteLine(k(4,5));
    }
    Console.ReadKey();
}

委托就讲到这里,重新概括一下就是

  1. 委托是一种类型
  2. 委托是一种类型与返回值相同函数的链表
  3. 委托使用+=,=,-=三个符号操作链表里的函数
  4. 调用委托就会依次调用委托链表里的所有函数
  5. 委托返回值默认是最后一个添加的函数返回值

关于事件(event)

事件是一种特殊的委托,事件需要与类结合起来理解。

首先明确关键字event在实例化委托时使用而非定义委托时使用。

委托定义与使用

delegate void func();
static func l;

事件定义与使用

delegate void func();
static event func l;

注意关键字的位置,在实例化的时候使用event;

事件需要与类结合起来,定义事件的类才可以调用事件

delegate void func();
class Program
{
    
    static void Main(string[] args)
    {
        Person.l();//这里报错Person.l”只能出现在 += 或 -= 的左边(从类型“Person”中使用时除外)
        Console.ReadKey();
    }
}
class Person{ 
public static event func l;//这里定义事件
    void Action() {
    l();//这里没有报错因为这里在定义事件的类里。
    }
}

之前讲过,委托就像是一串鞭炮。将函数链接与取消或者触发,可以自由使用。但是事件限定了只能由声明事件的类来触发。而链接函数的过程,可以看做一种通知。这里写一个黑人抬棺的例子帮助大家理解

using System;
using System.Data;
using System.IO;
namespace ConsoleApp3
{
    delegate void func(string name);
    class Program
    {
        
        static void Main(string[] args)
        {
            Person Hei1 = new Person("黑人小哥1");
            Person Hei2 = new Person("黑人小哥2");
            Person Hei3 = new Person("黑人小哥3");
            Person Hei4 = new Person("黑人小哥4");
            Person ming = new Person("小明");
            ming.died += Hei1.taiGuan;
            ming.died += Hei2.taiGuan;
            ming.died += Hei3.taiGuan;
            ming.died += Hei4.taiGuan;
            ming.fellFull += Hei1.seeFull;
            ming.fellFull += Hei2.seeFull;
            ming.fellFull += Hei3.seeFull;
            ming.fellFull += Hei4.seeFull;
            ming.fellHungry += Hei1.seeHungry;
            ming.fellHungry += Hei2.seeHungry;
            ming.fellHungry += Hei3.seeHungry;
            ming.fellHungry += Hei4.seeHungry;
            ming.EatDonut(10);
            Console.ReadKey();
        }
    }
    class Person{
        string name;
        public event func died;
        public event func fellFull;
        public event func fellHungry;
        public Person(string name) {
            this.name = name;
        }
        
        public void EatDonut(int i){
            if (i <= 10)
            {
               fellHungry(name);
            }
            else {
                if (i <= 100)
                {
                    fellFull(name);
                }
                else {
                    if (i > 100) {
                        died(name);
                    }
                }
            }
        }
        public void taiGuan(string name) {
            Console.WriteLine(this.name+"收到"+name+"死了,开始抬棺!奏乐!!");
        }
        public void seeHungry(string name)
        {
            Console.WriteLine(this.name + "看到" + name + "还很饿");
        }
        public void seeFull(string name)
        {
            Console.WriteLine(this.name + "看到" + name + "吃饱了");
        }
    }
}

我们只调用了,小明吃了10个甜甜圈,然后四个黑人小哥就都看到了。

ming.EatDonut(10);

我把代码改一下,大家可以试着运行一下

using System;
using System.Data;
using System.IO;
namespace ConsoleApp3
{
    delegate void func(string name);
    class Program
    {
        
        static void Main(string[] args)
        {
            Person Hei1 = new Person("黑人小哥1");
            Person Hei2 = new Person("黑人小哥2");
            Person Hei3 = new Person("黑人小哥3");
            Person Hei4 = new Person("黑人小哥4");
            Person ming = new Person("小明");
            ming.died += Hei1.taiGuan;
            ming.died += Hei2.taiGuan;
            ming.died += Hei3.taiGuan;
            ming.died += Hei4.taiGuan;
            ming.fellFull += Hei1.seeFull;
            ming.fellFull += Hei2.seeFull;
            ming.fellFull += Hei3.seeFull;
            ming.fellFull += Hei4.seeFull;
            ming.fellHungry += Hei1.seeHungry;
            ming.fellHungry += Hei2.seeHungry;
            ming.fellHungry += Hei3.seeHungry;
            ming.fellHungry += Hei4.seeHungry;
            int i;

            try {
                while (true){
                    Console.WriteLine("请输入小明吃甜甜圈的数量:");
                    i = int.Parse(Console.ReadLine());
                    ming.EatDonut(i);
                    Console.ReadKey();
                    Console.Clear();
                }
            }
            catch (Exception e) { 
            
            }
           
        }
    }
    class Person{
        string name;
        public event func died;
        public event func fellFull;
        public event func fellHungry;
        public Person(string name) {
            this.name = name;
        }
        
        public void EatDonut(int i){
            if (i <= 10)
            {
               fellHungry(name);
            }
            else {
                if (i <= 100)
                {
                    fellFull(name);
                }
                else {
                    if (i > 100) {
                        died(name);
                    }
                }
            }
        }
        public void taiGuan(string name) {
            Console.WriteLine(this.name+"收到"+name+"死了,开始抬棺!奏乐!!");
        }
        public void seeHungry(string name)
        {
            Console.WriteLine(this.name + "看到" + name + "还很饿");
        }
        public void seeFull(string name)
        {
            Console.WriteLine(this.name + "看到" + name + "吃饱了");
        }
    }
}

利用+=注册事件以后,+=后面的函数何时触发,就由事件声明的主体来控制。因为我们都想知道这件事情发生以后,我们要预期什么效果。而声明类之外的函数,我们只能选择接受"+=“或者拒绝接收”-=“,我们无法清空”=",或者调用该事件。

本文章面向于不会使用这两个特性的初学者,如果你有更好的想法,或者希望作者写一些关于编程其他特点的文章,欢迎留言。也欢迎大神指出错误,提出更好的见解。

转自:
Solita:https://zhuanlan.zhihu.com/p/413733828

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值