委托与事件
委托
委托定义
委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。
委托是安全封装方法的类型,类似于 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;

浙公网安备 33010602011771号