委托与事件

委托

委托定义

委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。
委托是安全封装方法的类型,类似于 C 和 C++ 中的函数指针。 与 C 函数指针不同的是,委托是面向对象的、类型安全的和可靠的。

什么时候使用委托

1、模板方法
2、需要方法作为参数
3、发布订阅

如何使用委托

  • 声明:修饰符 delegate关键字 返回类型 方法名(参数声明)
    public delegate void DGetData(int id);

  • 初始化:
    假如有一个方法void GetData(int i){}
    1、DGetData dGetData = new DGetData(new Program().GetData); --像类一样初始化,实例方法需要类初始化调用,静态方法不用
    2、DGetData dGetData =new Program().GetData;  --省略了new DGetData
    注意:初始化支持逆变和协变(如果参数是父类或者返回值是子类,也可以使用),例如:

      public  delegate object DGetData(string id); //注意:委托是与类同级的,所以不能在方法内部声明
      static void Main(string[] args)
      {
          DGetData dGetData = GetString;
          DGetData dGetData = GetInt;     //报错,逆变和协变不支持基本类型
      }
      public static string GetString(object id) { return string.Empty; }
      public static int GetInt(string id) { return 0; }
    
  • 调用:
    dGetData(1);等于dGetData.Invoke(1);

委托的内部实现

定义一个委托:public delegate object DGetData(string id);
反编译IL代码为:

.class nested public auto ansi sealed DGetData
	extends [System.Runtime]System.MulticastDelegate
{
	.method public hidebysig specialname rtspecialname 
		instance void .ctor (
			object 'object',
			native int 'method'
		) runtime managed 
	{
	} 

	.method public hidebysig newslot virtual 
		instance object Invoke (
			string id
		) runtime managed 
	{
	}

	.method public hidebysig newslot virtual 
		instance class [System.Runtime]System.IAsyncResult BeginInvoke (
			string id,
			class [System.Runtime]System.AsyncCallback callback,
			object 'object'
		) runtime managed 
	{
	} 

	.method public hidebysig newslot virtual 
		instance object EndInvoke (
			class [System.Runtime]System.IAsyncResult result
		) runtime managed 
	{
	}
}

可以看出编译器,实际生成了一个类(继承自MulticastDelegate,而它由继承Delegate),包含4个方法:分别是构造函数,同步调用Invoke和异步调用;
虽然委托数据都是从Delegate或者MulticastDelegate派生的,但是C#编译器不允许直接或间接的声明它们的子类。

  • MulticastDelegate中三个非公共字段:
    .field assembly object _target:目标对象:静态方法为null,实例方法的实例对象
    .field private object _invocationList:构建委托链时的委托数组
    .field assembly native int _methodPtr:标识回调的方法
    其中_target、_methodPtr是Delegate中定义的
il代码:
	nested:镶嵌类	
	auto:程序的加载是由CLR来管理内存的	
	ansi:是为了在没有托管代码与托管代码之间实现无缝转换。这里主要指C、C++代码等	
	hidebysig:表示当把此类作为基类,存在派生类时,此方法不被继承	
	specialname:方法是特殊的,名字描述是怎样特殊。
	rtspecialname:运行时应该检查名字的编码,该方法拥有 CLR 内部使用的特殊名称,必须和 specialname 一起使用。
	runtime:该方法由 CLR 自动生成。只有 mscorlib.dll 中的某些方法带有此标记。
	managed:托管代码。(默认)
	newslot:必须和 virtual 一起使用,添加该关键字的虚方法不会 override 基类中同名(含签名)虚方法,而是在 v-table 中创建一个新槽(new slot)。
	instance: 表示是实例方法  

委托链

Delegate定义了静态方法Combine和Remove方法,内部分别调用RemoveImpl和CombineImpl方法,其中MulticateDelegate重写了RemoveImpl和CombineImpl方法,用于操作_invocationList属性,是用来链接和移除链接的方法,但是这两个方法都是返回一个全新的对象,而不是在原来的对象上进行操作。
其中C#重载了+=和-=操作符,这些操作符内部调用的是Combine和Remove方法。
Remove:如果移除完,则会返回null值;从0开始扫描,删除第一个
Combine:如果传入null值,则返回原值

internal delegate void Feedback(Int32 value);
static void Main(string[] args)
{
	var fb1 = new Feedback(FeedbackToConsole);
	Feedback fbChain = null;
	fbChain = (Feedback)Delegate.Combine(fbChain, fb1);
	fbChain = (Feedback)Delegate.Remove(fbChain,fb1);
	fbChain += fb1;
	fbChain -= fb1;

	fbChain.Invoke(2);
}
static void FeedbackToConsole(Int32 value){    }

调用Invoke时,如果委托是有返回值的,则只返回最后调用方法的返回值,其中如果有一个方法报异常或阻塞,则后续的方法都会终止,可以使用GetInvocationList方法,遍历委托,对每个方法进行分别调用

预定义的委托

  • 无返回值:
    Action :public delegate void Action();重载了几个参数版本,一般可以满足日常的使用
  • 有返回值:
    Func:public delegate TResult Func(); //通上重载了几个版本

lambda表达式

什么时候使用

一般方法是临时方法,不需要重用,可以考虑使用此表达式

如何使用

  • 定义
    Func<int,int, int> func = (int x,int y) => { return x+y; };
    ()中写参数,=>标识,{}中为语句块;如果参数只有一个,则可以省略括号,其中数据类型可以省略,写成(x,y),会根据委托转换成响应的类型;如果语句块只有一条语句,可以省略大括号,写成(x,y)=>x+y;这种的又称为表达式lambda,前一种称为语句lambda;
    编译器遇到lambda表达式,会把它编译成一个类,其中参数和局部变量会被转变成该类的字段。
    表达式lambda会被生成表达式树:Expression<Func<int,int, int>> expression = (int x,int y) => x+y;

事件

对委托的封装:1、只有在包装类内才能使用赋值操作符,其他类只能添加或移除;2、只有在包装类内才能触发事件通知,Invoke
在类内部,方法外面定义:public static event Get EGet = () => 1;

posted @ 2020-07-02 20:22  drahab  阅读(91)  评论(0)    收藏  举报