事件与委托

Posted doNet源计划

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事件与委托相关的知识,希望对你有一定的参考价值。

(1) 什么是委托(Delegates)?

委托是一个对象,它知道如何调用一个方法。

委托的定义: 委托类型定义了委托实例可以调用的那类方法,具体来说,委托类型定义了方法的返回类型和参数。

  1. delegatestringHello(string name);
  2. stringSplicing(string name){return $"hello,{ name }.";}

 

(2) 委托实例

  • 把方法赋值给委托变量的时候就创建了委托实例。
  1. Hello hello =Splicing;

 

  • 调用
  1. string resultName = hello("bob");//输出 hello bob

 

  • 委托的实例其实就是调用者的委托:调用者调用委托,然后委托调用目标方法。
  • 间接的把调用者和目标方法解耦合了。
  • 方法是在运行时才赋值给委托变量的。

(3) 多播委托

所有的委托实例都具有多播的能力。一个委托实例可以引用一组目标方法。

引用一组方法的示例:

  • +和+= 还有 -和-=操作符号可以合并和解除委托实例。
    DemoDelegate dd = DemoMethond1; DemoDelegate dd += DemoMethond2;
  • 调用委托dd就回调用DemoMethond1和DemoMethond2
  • 委托的调用顺序与他们的定义顺序一致
    • 和 -=会把右边的委托从左边的委托里移除
  1. DemoDelegate dd -=DemoMethond1;

 

 

  • 委托变量使用+或+=操作符时,其操作数可以是null。就相当于把一个新的值赋给了委托变量。

    DemoDelegate dd = null; dd += DemoMethond1;

相当于

 

  1. dd =DemoMethond1;

 

  • 对单个目标方法的委托变量使用-=操作符时,就相当于把null值赋值给了委托变量。
  • 委托是不可变的
  • 使用-=或+=操作符时,实际上时创建了新的委托实例,并把它赋给当前的委托变量。
  • 如果多播委托的返回值不是void,那么调用者从最后一个被调用的方法来接收返回值。前面的方法仍然会被调用,但是其返回值就被弃用了。
  • 所有的委托类型都派生于system.MulticastDelegate,而它又派生于system.delegate
  • c#会把作用于委托的+、-、-=、+=操作编译成使用system.delegate的combine和remove两个静态方法。

实例方法目标和静态方法目标

  • 当一个实例方法被赋值给委托对象的时候,这个委托对象不仅要保留着对方法的引用,还要保留着方法所属实例的引用。
  • system.delegate的Target属性就代表着这个实例。
  • 如果引用的是静态方法,那么Target属性值就是null。
  1. publicdelegatevoidReporter(int result);
  2. publicclass A
  3. {
  4. publicvoidAReporter(int result)
  5. {
  6. Console.WriteLine(result);
  7. }
  8. }
  9. publicclassProgram
  10. {
  11. staticvoidMain()
  12. {
  13. A a =new A();
  14. Reporter r = x.AReporter;
  15. r(50);
  16. Console.WriteLine(r.Target== x);//True
  17. Console.WriteLine(p.Method);//void AReporter(Int32)
  18. }
  19. }

 

(4) 泛型委托

  • 委托类型可以包含泛型类型
  1. publicdelegate T Mydelegate<T>(T parameter);

 

Func和Action委托

  • 使用泛型委托,他们可以调用的方法可以拥有任意返回值类型和任意合理数量的参数。
  1. //1.这里表示的是,只有一个带泛型T返回值类型的委托
  2. delegateTResultFunc<outTResult>();
  3. //2.这里表示的是,有一个带泛型T返回值类型和一个带泛型入参的委托(入参数量最多16个)
  4. delegateTResultFunc<in T,outTResult>(T parameter);
  5. //3.这里表示的是,一个无参无返回值类型的委托
  6. delegatevoidAction();
  7. //4.这里表示的是,一个入参无返回值类型的委托(入参数量最多16个)
  8. delegateTResultFunc<in T,outTResult>(T parameter);

 

(5) 委托and接口

  • 委托可以解决的问题,接口都可以解决。
  • 什么情况下更适合委托而不是接口呢?当下列条件满足其中之一时:
    • 接口只能定义一个方法
    • 需要多播能力
    • 订阅者需要多次实现接口
  1. class A :ICalculation
  2. {
  3. publicintCalculation(int x)=> x*x;
  4. }
  5. class B :ICalculation
  6. {
  7. publicintCalculation(int x)=> x*x*x;
  8. }

 

<

(6) 委托的兼容性

  • 委托类型之间互不相容,即使方法签名一样
  1. delegatevoidDemo1();
  2. delegatevoidDemo2();
  3. Demo1 d1 =Method1;
  4. Demo2 d2 = d1;// error

 

  • 如果委托实例拥有相同的方法目标,那么委托实例就认为是相等的。
  • 当你调用一个方法时,你提供的参数(argument)可以比方法的参数(parameter)定义更具体。
  • 委托可以接受比她的方法目标更具体的参数类型,这个叫ContraVariance
  • 和泛型参类型一样,委托的Variance仅支持引用转换
  1. delegatevoidStringAction(string s);
  2. classDemo
  3. {
  4. staticvoidMain()
  5. {
  6. StringAction sa =newStringAction(ActOnObject);
  7. sa("hello");
  8. }
  9. staticvoidActOnObject(object o)=>Console.WriteLine(o);//hello
  10. }

 

  • 调用方法时,你可以得到一个比请求的类型更具体的类型返回结果。
  • 委托的目标方法可以返回比委托描述里更具体的类型的返回结果Covariance.
  • Covariance,out; ContraVariance,in;
    1. delegateobjectObjectRetriever(string s);
    2. classDemo
    3. {
    4. staticvoidMain()
    5. {
    6. ObjectRetriever o =newObjectRetriever(RetrieverString);
    7. object result = o();
    8. Console.WriteLine(result);
    9. }
    10. staticstringRetrieverString()=>"hello";
    11. }

以上是关于事件与委托的主要内容,如果未能解决你的问题,请参考以下文章

事件与委托

C#编程之委托与事件四

直接事件与事件委托

理解Javascript中的事件绑定与事件委托

冒泡事件与事件委托

C# 再次理解委托事件与函数作为参数