一:什么叫委托

通过反射发现,委托其实是一个类,继承自System.MulticastDelegate,但是System.MulticastDelegate这个类是特殊类,不能被继承

二:委托的声明

 public delegate void NoReturnNoParaOutClass();
public class MyDelete
{
public delegate void NoReturnNoPara<T>(T t);
public delegate void NoReturnNoPara();
public delegate void NoReturnWithPara(int x, int y);
public delegate int WithReturnNoPara();
public delegate string WithReturnWithPara(out int x, ref int y);
}

委托可以声明在类外面,可以声明再类里面

三:委托的实例和调用

      private int GetSomething()
{
return ;
}
private int GetSomething2()
{
return ;
} private int GetSomething3()
{
return ;
}
private void DoNothing()
{
Console.WriteLine("This is DoNothing");
}
private static void DoNothingStatic()
{
Console.WriteLine("This is DoNothingStatic");
}
public string ParaReturn(out int x, ref int y)
{
throw new Exception();
}
//多种途径实例化,要求传递一个参数类型,返回值都跟委托一致的方法
{
WithReturnWithPara method = new WithReturnWithPara(ParaReturn);
int x = ;
int y = ;
var dd = method.Invoke(out x, ref y);
}
//begininvoke
{
WithReturnNoPara method = new WithReturnNoPara(this.GetSomething);
int iResult = method.Invoke();
iResult = method();
var result = method.BeginInvoke(null, null);//异步调用
method.EndInvoke(result);
}
{
NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);
//委托实力的调用,参数和委托约束的一致
method.Invoke(); //1
//method(); //2
//method.BeginInvoke(null, null); //3
//this.DoNothing(); //1,2,3都等同于this.DoNothing }
{
NoReturnNoPara method = new NoReturnNoPara(DoNothingStatic);
}
{
NoReturnNoPara method = new NoReturnNoPara(Student.StudyAdvanced);
}
{
NoReturnNoPara method = new NoReturnNoPara(new Student().Study);
}

四:为什么要使用委托

有时候我们声明一个方法,直接调用蛮好的,为啥还要使用委托,然后还要先声明,再实例化,再inovke调用呢?

下面我们举个例子,比如一个人问好这件事情,不同人问候方式不一样,我们会先定义一个类型,如枚举

public enum PeopleType
{
Chinese,
America,
Japanese
}

然后通过不同的类型来判断问候方式不同,如下

        /// 为不同的人,进行不同的问候
/// 传递变量--判断一下----执行对应的逻辑
/// </summary>
/// <param name="name"></param>
/// <param name="peopleType"></param>
public void SayHi(string name, PeopleType peopleType)
{
switch (peopleType)
{
case PeopleType.Chinese:
Console.WriteLine($"{name}晚上好");
break;
case PeopleType.America:
Console.WriteLine($"{name},good evening");
break;
case PeopleType.Japanese:
Console.WriteLine($"{name},&&%*^^***@@@&&&&");
break;
default:
throw new Exception("wrong peopleType"); //遇到异常报错
}
}

这样做的好处是:以后如果增加公共逻辑等比较容易,但是如果类型比较多,这个方法会变成无限制改动,导致方法难以维护,于是很多人想着增加分支,就增加方法--不影响别的方法的思路来改善

  public void SayHiChinese(string name)
{
Console.WriteLine($"{name}晚上好");
}
public void SayHiJapanese(string name)
{
Console.WriteLine($"{name},&&%*^^***@@@&&&&");
}
public void SayHiAmerican(string name)
{
Console.WriteLine($"{name},good evening");
}

然后上层判断调用

这样做的好处是:修改某个方法--不影响别的方法 ,但是缺点却是:增加公共逻辑---多个方法就有很多重复代码

那么我们想:既增加逻辑方便,又维护简单,鱼肉熊掌,如何兼得呢?

我们可以把相应的逻辑做为参数传进来,这样就解决了我们的问题

具体我们可以按照以下来做:

  public void SayHiPerfact(string name, SayHiDeletegate method)
{
Console.WriteLine("增加开始日志");
method.Invoke(name);
Console.WriteLine("增加结束日志");
}
public delegate void SayHiDeletegate(string name);

然后调用的时候如下:

  SayHiDeletegate method = new SayHiDeletegate(SayHiChinese);

这样就做到了

1:逻辑解耦,方便维护

2:代码重构,去掉重复

其实这也是我们选择使用委托的两大优点

注意:以上我们纯粹为了定义委托而定义委托,其实框架已经我们帮我们定义了Action 和Func这两个委托,Action是没有返回值,Func是有返回值的,这两个委托类已经足够我们使用了,所以有时候我们使用的时候,没有必要自己再去定义,而直接使用即可

1:Action: 系统提供的,0-16个泛型参数,不带返回值的 委托

//Action 系统提供的,0-16个泛型参数,不带返回值的 委托
Action action0 = new Action(DoNothing);
Action action1 = this.DoNothing; //语法糖,就是编译器帮我们添加上new Action
Action<int> action2 = this.ShowInt;

2:Func 系统提供的,0-16个泛型参数,带一个返回值的 委托

 //Func 系统提供的,0-16个泛型参数,带返回值的 委托
Func<int> func = this.GetSomething2;
func.Invoke(); Func<int, string> func1 = this.ToString;
func1.Invoke();

3:系统或者框架为什么要封装这样的方法?第一步我们定义一个这样的方法,需要传入一个Action

private void DoAction(Action act)
{
act.Invoke();
}

然后我们可以这样调用:

Action action0 = this.DoNothing;
NoReturnNoPara method = this.DoNothing;
// this.DoAction(method); //这样就不行,因为类型不一致
this.DoAction(action0);

但是我们不能使用 this.DoAction(method),因为:委托的本质是类,action和NoReturnNoPara是不同的类,虽然实例化都可以传递相同的方法,但是没有父子关系,所以不能替换,就像student和teacher两个类,实例化都是传递id/name,但是二者不能替换

所以:框架提供这种封装,自然是希望大家都统一使用Action/Func,但是之前还有很多委托,至今再框架中还能看到,因为.net向前兼容,以前的版本去不掉的,保留着,这是历史包袱,此后我们就不要定义新的委托了!

五:多播委托

委托是一个类,然后继承于MulticastDelegate(多播委托),但是这个类是特殊类,所以任何一个委托都是多播委托类型的子类

 {
//多播委托:任何一个委托都是多播委托类型的子类,可以通过+=去添加方法
//多播委托如果中间出现未捕获的异常,方法链直接结束
//多播委托:一个变量保存多个方法,可以增减;invoke的时候可以按顺序执行
//+= 为委托实例按顺序增加方法,形成方法链,Invoke时,按顺序依次执行系列方法
Student studentNew = new Student(); NoReturnNoPara method = new NoReturnNoPara(this.DoNothing); //普通的DoNothing方法
method += new NoReturnNoPara(this.DoNothing); //普通的DoNothing方法
method += new NoReturnNoPara(DoNothingStatic); //静态方法
method += new NoReturnNoPara(Student.StudyAdvanced); //静态方法
method += new NoReturnNoPara(new Student().Study);//不是同一个实例,所以是不同的方法
method += new NoReturnNoPara(studentNew.Study);
method.Invoke();
//method.BeginInvoke(null, null);//委托里面如果有多个Target,则不能异步调用,多播委托是不能异步的 foreach (Action item in method.GetInvocationList()) //得到委托实例的target方法 ,静态方法target为null
{
item.Invoke();
//item.BeginInvoke(null, null);
} //-= 为委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除且只移除一个,没有也不异常
method -= new NoReturnNoPara(this.DoNothing);
method -= new NoReturnNoPara(DoNothingStatic);
method -= new NoReturnNoPara(Student.StudyAdvanced);
method -= new NoReturnNoPara(new Student().Study); //去不掉,原因是不同的实例的相同方法,并不吻合
method -= new NoReturnNoPara(studentNew.Study);
method.Invoke();
}
{
WithReturnNoPara method = new WithReturnNoPara(this.GetSomething);
method += new WithReturnNoPara(this.GetSomething2);
method += new WithReturnNoPara(this.GetSomething3);
int iResult = method.Invoke();//多播委托带返回值,结果以最后的为准,所以一般多播委托用的是不带返回值的
}

注意:

+= 为委托实例按顺序增加方法,形成方法链,Invoke时,按顺序依次执行系列方法

-= 为委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合(new Student().Study如果声明两次则不属于吻合)的,移除且只移除一个,没有也不异常

多播委托其实就是观察者模式的缩影。

比如一只猫叫了一声,然后需要触发一系列的动作,比如老鼠跑,孩子哭,狗叫等等

如果我们把这些都写在猫的miao的方法中,如下:

public void Miao()
{
Console.WriteLine("{0} Miao", this.GetType().Name); new Mouse().Run();
new Baby().Cry();
new Mother().Wispher();
//new Brother().Turn();
new Father().Roar();
new Neighbor().Awake();
new Stealer().Hide();
new Dog().Wang();
}

上面的代码:

依赖太重,依赖多个类型,任何类型的变化都得修改猫
职责耦合,猫不仅自己miao,还得找各种对象执行各种动作甚至控制顺序
任意环节增加或者减少调整顺序, 都得修改猫

那么我们可以修改代码,然后Cat的这类中,增加一个委托,然后猫只是自己叫,具体的其他的动作,只需要执行这个多播委托即可,代码如下:

 //猫 叫一声   触发一系列后续动作
//多了个 指定动作 正是这个不稳定 封装出去 甩锅
public MiaoDelegate MiaoDelegateHandler;
public void MiaoNew()
{
Console.WriteLine("{0} MiaoNew", this.GetType().Name);
if (this.MiaoDelegateHandler != null)
{
this.MiaoDelegateHandler.Invoke();
}
}

然后外面调用的时候可以直接通过下面:

 {
Cat cat = new Cat();
cat.MiaoDelegateHandler += new MiaoDelegate(new Mouse().Run);
cat.MiaoDelegateHandler += new MiaoDelegate(new Baby().Cry);
cat.MiaoDelegateHandler += new MiaoDelegate(new Mother().Wispher);
cat.MiaoDelegateHandler += new MiaoDelegate(new Brother().Turn);
cat.MiaoDelegateHandler += new MiaoDelegate(new Father().Roar);
cat.MiaoDelegateHandler += new MiaoDelegate(new Neighbor().Awake);
cat.MiaoDelegateHandler += new MiaoDelegate(new Stealer().Hide);
cat.MiaoDelegateHandler += new MiaoDelegate(new Dog().Wang);
cat.MiaoNew();
Console.WriteLine("***************************");
}

这样猫就减少了依赖,而且猫叫后,各种动作的顺序也可以随便改变,而不会改变猫叫的方法!

六:事件

事件:是带event关键字的委托的实例,event可以限制变量被外部调用/直接赋值
event:限制权限,只允许在事件声明类里面去invoke和赋值,不允许外面,甚至子类都能运用

event事件只能声明在类中,而委托可以声明在类外面

比如我们在cat类中定义一个事件

 public event MiaoDelegate MiaoDelegateHandlerEvent;
public void MiaoNewEvent()
{
Console.WriteLine("{0} MiaoNewEvent", this.GetType().Name);
if (this.MiaoDelegateHandlerEvent != null)
{
this.MiaoDelegateHandlerEvent.Invoke();
}
}

然后只能在cat类中中进行invoke调用,如果我们定义一个子类继承cat类,如下:

public class ChildClass : Cat
{
public void Show()
{
this.MiaoDelegateHandlerEvent += null;
if (this.MiaoDelegateHandlerEvent != null)//子类也不能调用,调用报错
{
this.MiaoDelegateHandlerEvent.Invoke();
}
}
}
this.MiaoDelegateHandlerEvent.Invoke(); 这样是不能使用的,Invoke这个是完全不能调用的!

委托和事件的区别与联系?
委托是一个类型,比如Student
事件是委托类型的一个实例,加上了event的权限控制 比如学生加菲猫,是一个确切的实体

然后事件也可以通过下面来调用多个方法

{
Cat cat = new Cat();
//cat.MiaoDelegateHandler += new MiaoDelegate(new Mouse().Run);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Baby().Cry);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Mother().Wispher);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Brother().Turn);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Father().Roar);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Neighbor().Awake);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Stealer().Hide);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Dog().Wang);
cat.MiaoNewEvent();
Console.WriteLine("***************************");
}

下面写一个标准的的事件,事件一般分为三种:

1:事件的发布者:发布事件,并且在满足条件时候触发事件

2:事件的订户:关注事件,事件发生后,自己做出相应的动作

3:事件的订阅:把订户和发布者的事件关联起来

     /// <summary>
/// 委托是一种类型,静态类不能被继承,所以委托不能声明静态,
/// event只是一个实例,所以可以生成实例
/// </summary>
class EventStandard
{
/// <summary>
/// 订阅:把订户和发布者的事件关联起来
/// </summary>
public static void Show()
{
iPhoneX phone = new iPhoneX()
{
Id = ,
Tag = "1.0"
};
// 订阅:把订户和发布者的事件关联起来
phone.DiscountHandler += new Student().Buy;
phone.DiscountHandler += new Teacher().Notice; phone.Price = ; }
/// <summary>
/// 订户:关注事件,事件发生后,自己做出对应的动作
/// </summary>
public class Student
{
public void Buy(object sender, EventArgs e)
{
iPhoneX phone = (iPhoneX)sender;
Console.WriteLine($"this is {phone.Tag} iphoneX");
XEventArgs args = (XEventArgs)e;
Console.WriteLine($"之前的价格{args.OldPrice}");
Console.WriteLine($"限制的价格{args.NewPrice}");
Console.WriteLine("立马买!!");
}
}
public class Teacher
{
public void Notice(object sender, EventArgs e)
{
iPhoneX phone = (iPhoneX)sender;
Console.WriteLine($"this is {phone.Tag} iphoneX");
XEventArgs args = (XEventArgs)e;
Console.WriteLine($"之前的价格{args.OldPrice}");
Console.WriteLine($"限制的价格{args.NewPrice}");
Console.WriteLine("立马买!!");
}
} /// <summary>
/// 事件参数 一般会为特定的事件去封装个参数
///订户:Teacher/Student:关注事件,事件发生后,自己做出对应的动作
/// </summary>
public class XEventArgs : EventArgs
{
public int OldPrice { set; get; }
public int NewPrice { set; get; }
} /// <summary>
/// 事件的发布者,发布事件,并且在满足条件时候触发事件
/// </summary>
public class iPhoneX
{
public int Id { set; get; }
public string Tag { set; get; }
public int Price
{
set
{
if (value < this._price)
{
this.DiscountHandler?.Invoke(this, new XEventArgs() { OldPrice = this._price, NewPrice = value });
this._price = value;
}
}
get { return this._price; }
} private int _price; /// <summary>
/// 打折事件
///
/// </summary>
public event EventHandler DiscountHandler;
}
}

EventHandler:框架自带,表示将用于处理不具有事件数据的事件的方法。EventHandler(object sender, EventArgs e)

下面这个例子也很经典

   /// <summary>
/// 妈妈做好饭,触发爸爸和儿子一起吃饭,并且妈妈会把对应的菜单同时告知爸爸和儿子
/// </summary>
public class MyEvenStandard
{
public class Meal
{
public static void FinishMeal()
{
Mom mom = new Mom();
mom.EatHandler += (new Dad()).Eat;
mom.EatHandler += (new Son()).Eat;
mom.Cook();
}
} public class Mom
{
//EventHandler(object sender, EventArgs e) 事件的原型,里面sender则是发布者自己,e:是发布者的交互参数
public event EventHandler EatHandler;
public void Cook()
{
Console.WriteLine("开始吃饭了");
EatHandler?.Invoke(this, new MenuArags() { Fruid = "orange", Greens = "green pepper", Meat = "fish" });
}
}
/// <summary>
/// 爸爸
/// </summary>
public class Dad
{
public void Eat(object sender, EventArgs args)
{
var menArgs = (MenuArags)args;
Console.WriteLine($"儿子,我们今天晚上次:{menArgs.Greens},{menArgs.Fruid},{menArgs.Meat}");
}
} /// <summary>
/// 儿子
/// </summary>
public class Son
{
public void Eat(object sender, EventArgs args)
{
var menArgs = (MenuArags)args;
Console.WriteLine($"爸爸,我们今天晚上次:{menArgs.Greens},{menArgs.Fruid},{menArgs.Meat}");
}
} public class MenuArags : EventArgs
{
/// <summary>
/// 水果
/// </summary>
public string Fruid { set; get; } /// <summary>
/// 青菜
/// </summary>
public string Greens { set; get; } public string Meat { set; get; }
} }

C# 委托 事件的更多相关文章

  1. C# ~ 从 委托事件 到 观察者模式 - Observer

    委托和事件的部分基础知识可参见 C#/.NET 基础学习 之 [委托-事件] 部分: 参考 [1]. 初识事件 到 自定义事件: [2]. 从类型不安全的委托 到 类型安全的事件: [3]. 函数指针 ...

  2. C#委托,事件理解入门 (译稿)

    原文地址:http://www.codeproject.com/Articles/4773/Events-and-Delegates-Simplified 引用翻译地址:http://www.cnbl ...

  3. 关于ios使用jquery的on,委托事件失效

    $('.parents').on("click",'.child',function(){}); 类似上面这种,在ios上点击"child"元素不会起作用,解决 ...

  4. Observer设计模式中-委托事件-应用在消息在窗体上显示

    Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...

  5. Asp.net用户控件和委托事件

    在Asp.net系统制作过程中,门户类型的网站,我们可以用DIV+CSS+JS+Ajax全部搞定,但是一旦遇到界面元素比较复杂的时候,还是UserControl比较方便一些,各种封装,各种处理,然后拖 ...

  6. jQuery里面的普通绑定事件和on委托事件

    以click事件为例: 普通绑定事件:$('.btn1').click(function(){}绑定 on绑定事件:$(document).on('click','.btn2',function(){ ...

  7. c#基础学习笔记-----------委托事件

    这里有一个比较简单的委托实例应用(跨窗体操作控件) http://blog.csdn.net/bdstjk/article/details/7004035 还有一个比较详细的介绍并深入理解委托事件的讲 ...

  8. c#委托事件入门--第二讲:事件入门

    上文 c#委托事件入门--第一讲:委托入门 中和大家介绍了委托,学习委托必不可少的就要说下事件.以下思明仍然从事件是什么.为什么用事件.怎么实现事件和总结介绍一下事件 1.事件是什么:. 1.1 NE ...

  9. Unity C#笔记 委托&事件

    C#的委托与事件搭配,即是观察者模式的一种实现. 因为观察者模式的原理很易懂,不作多讲,本文纯粹用于记录语法. delegate(委托) //声明没有参数,没有返回值的委托类型XXXX public ...

  10. JavaScript中事件委托(事件代理)详解

    在JavaScript的事件中,存在事件委托(事件代理),那么什么是事件委托呢? 事件委托在生活中的例子: 有三个同事预计会在周一收到快递.为签收快递,有两种办法:一是三个人在公司门口等快递:二是委托 ...

随机推荐

  1. Java基础之Java常用类--Object类,字符串相关类,包装类,日期相关类,数字相关类

    Java是一种面向对象的语言,也就是将万事万物可以描述为对象,特点如下: 1.面向对象是常见的一种思考习惯,符合人们的思考习惯.2.面向对象的出现,将复杂的事情简单化.3.面向对象的出现,将之前过程中 ...

  2. java 保证程序安全退出

    以前在开发时只知道依靠数据库事务来保证程序关闭时数据的完整性. 但有些时候一个业务上要求的原子操作,不一定只包括数据库,比如外部接口或者消息队列.此时数据库事务就无能为力了. 这时我们可以依靠java ...

  3. FPGA中带优先级的if else if与不带优先级的case的探讨

    我们知道在书本上都说让我们尽量使用不带优先级的的数据选择器,今天我们就来探讨一下二者的区别. 例子1:带优先级的的数据选择器,综合成功,且没有任何警告. module detection_prio # ...

  4. scrapy 中 xpath 用string方法提取带有空格符解决方法

    注释掉的是刚开始的代码,匹配的全是带空格的,replace替换不了空格 后面加上了normalize-space()  匹配到的文本内容变成了可replace 问题解决

  5. C/C++ 多线程机制

    一.C/C++多线程操作说明 C/C++多线程基本操作如下: 1. 线程的建立结束 2. 线程的互斥和同步 3. 使用信号量控制线程 4. 线程的基本属性配置 在C/C++代码编写时,使用多线程机制, ...

  6. jsp进阶

    Jsp,前段的数据读取到后端 获取前段输入框数据 request.getParameter(前段输入框的name值) Request.代表转发,(代码在服务器内部执行的一次性请求,url地址不会发生改 ...

  7. centos上ftp服务器的简易安装部署

    申明:本示例为centos7 开启ftp服务命令为:systemctl start vsftpd 关闭防火墙命令为systemctl stop firewalld 7版本以下开启ftp服务器为 ser ...

  8. 阿里启动新项目:Nacos,比 Eureka 更强!

    什么是 Nacos? Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台. Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提 ...

  9. NopCommerce用.net core重写ef

    最近看了NopCommerce源码,用core学习着写了一个项目,修改的地方记录下.项目地址 NopCommerce框架出来好久了.18年的第一季度 懒加载出来后也会全部移动到.net core.那么 ...

  10. [译]聊聊C#中的泛型的使用(新手勿入)

    写在前面 今天忙里偷闲在浏览外文的时候看到一篇讲C#中泛型的使用的文章,因此加上本人的理解以及四级没过的英语水平斗胆给大伙进行了翻译,当然在翻译的过程中发现了一些问题,因此也进行了纠正,当然,原文的地 ...