委托
什么是委托?委托与事件有什么关系呢?先来看一下委托的定义,代码如下: 1
2
3
4
5
6
7
8
9
10
11
12
13namespace DotnetExample
{
//定义一个委托
public delegate void VoidCallback();
// Console必须要一个Main函数才能编译通过
class Program
{
static void Main(string[] args)
{
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15.class public auto ansi sealed DotnetExample.VoidCallback extends [System.Runtime]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname instance default void .ctor([System.Runtime]System.Object 'object', native int 'method') runtime managed
{
} // End of method System.Void DotnetExample.VoidCallback::.ctor(System.Object,System.IntPtr)
.method public hidebysig newslot virtual instance default void Invoke() runtime managed
{
} // End of method System.Void DotnetExample.VoidCallback::Invoke()
.method public hidebysig newslot virtual instance default [System.Runtime]System.IAsyncResult BeginInvoke(class [System.Runtime]System.AsyncCallback callback, [System.Runtime]System.Object 'object') runtime managed
{
} // End of method System.IAsyncResult DotnetExample.VoidCallback::BeginInvoke(System.AsyncCallback,System.Object)
.method public hidebysig newslot virtual instance default void EndInvoke(class [System.Runtime]System.IAsyncResult result) runtime managed
{
} // End of method System.Void DotnetExample.VoidCallback::EndInvoke(System.IAsyncResult)
} // End of class DotnetExample.VoidCallback
其中重要的几个函数:
- 构造函数 .ctor(Type, String), .ctor(Object String),当更一个委托赋值(=)时,会根据方法类型选择调用哪个构造函数,静态的函数调用第一种形式的构造函数,如果是成员函数的则会调用第二种形式的构造函数
- Combine函数, 当使用+=的时候则会调动Combine将一个委托追加到调用链的后面。
- CreateDelegate静态函数,更具各种类型创建委托
- GetInvocationList函数,获取委托链上的说有委托
- Remove函数,当使用-=的时候回调用Remove函数,它跟Combine函数是一对的,一个负责添加委托,一个负责移除委托。
上面定义了一个VoidCallback委托,下面我们来使用一下这个委托,代码如下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38namespace DotnetExample
{
class Program
{
static void Main(string[] args)
{
new DelegateExample().Init();
}
}
public delegate void VoidCallback();
class DelegateExample
{
public VoidCallback voidCallback;
public void Init()
{
voidCallback = TestCallback;
voidCallback += TestStaticCallback;
voidCallback.Invoke();
voidCallback -= TestCallback;
voidCallback -= TestStaticCallback;
}
void TestCallback()
{
Console.WriteLine("TestCallback is called.");
}
static void TestStaticCallback()
{
Console.WriteLine("TestStaticCallback is called.");
}
}
}
上面这些函数都是Delegate类里面的功能函数,其中“=”运算换转换成构造函数,“+=”会转换成Combine函数,“-=”会被转换成Remove函数。那么Invoke函数是哪里来的呢?在上面我们定义了一个委托,编译后给我们生成了一个类,这个类里面包含了3个函数其中一个就是Invoke,还是两个是BeginInvoke和EndInvoke函数,这几个函数有什么区别呢?Invoke是同步函数在主线程执行而BeginInvoke和EndInvoke是用于异步调用的一对函数在其他线程运行。BeginInvoke和EndInvoke如何使用,可以参考https://www.cnblogs.com/canger/p/5938591.html。
事件
我们还是通过CIL来了解事件吧!先定义个事件看看转换后的CIL代码是什么样的,事件定义代码如下: 1
2
3
4
5
6
7
8
9
10
11
12
13namespace DotnetExample
{
public delegate void VoidCallback();
class Program
{
public static event VoidCallback eventFields;
static void Main(string[] args)
{
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66.class private auto ansi beforefieldinit DotnetExample.Program extends [System.Runtime]System.Object
{
// eventFields这个字段被转换成里private的访问权限
.field private static class DotnetExample.VoidCallback eventFields
// 生成了一个add_xxxx(字段名)的函数
.method public hidebysig specialname static default void add_eventFields(class DotnetExample.VoidCallback 'value') cil managed
{
// Method begins at Relative Virtual Address (RVA) 0x2050
// Code size 39 (0x27)
.maxstack 3
.locals init(class DotnetExample.VoidCallback V_0, class DotnetExample.VoidCallback V_1, class DotnetExample.VoidCallback V_2)
IL_0000: ldsfld class DotnetExample.VoidCallback DotnetExample.Program::eventFields
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldarg.0
IL_000a: call [System.Runtime]System.Delegate class [System.Runtime]System.Delegate::Combine([System.Runtime]System.Delegate, [System.Runtime]System.Delegate)
IL_000f: castclass class DotnetExample.VoidCallback
IL_0014: stloc.2
IL_0015: ldsflda class DotnetExample.VoidCallback DotnetExample.Program::eventFields
IL_001a: ldloc.2
IL_001b: ldloc.1
IL_001c: call !!0 class [System.Threading]System.Threading.Interlocked::CompareExchange<class DotnetExample.VoidCallback>(!!0&, !!0, !!0)
IL_0021: stloc.0
IL_0022: ldloc.0
IL_0023: ldloc.1
IL_0024: bne.un.s IL_0006
IL_0026: ret
} // End of method System.Void DotnetExample.Program::add_eventFields(DotnetExample.VoidCallback)
// 生成了一个remove_xxxx(字段名)的函数
.method public hidebysig specialname static default void remove_eventFields(class DotnetExample.VoidCallback 'value') cil managed
{
// Method begins at Relative Virtual Address (RVA) 0x2084
// Code size 39 (0x27)
.maxstack 3
.locals init(class DotnetExample.VoidCallback V_0, class DotnetExample.VoidCallback V_1, class DotnetExample.VoidCallback V_2)
IL_0000: ldsfld class DotnetExample.VoidCallback DotnetExample.Program::eventFields
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldarg.0
IL_000a: call [System.Runtime]System.Delegate class [System.Runtime]System.Delegate::Remove([System.Runtime]System.Delegate, [System.Runtime]System.Delegate)
IL_000f: castclass class DotnetExample.VoidCallback
IL_0014: stloc.2
IL_0015: ldsflda class DotnetExample.VoidCallback DotnetExample.Program::eventFields
IL_001a: ldloc.2
IL_001b: ldloc.1
IL_001c: call !!0 class [System.Threading]System.Threading.Interlocked::CompareExchange<class DotnetExample.VoidCallback>(!!0&, !!0, !!0)
IL_0021: stloc.0
IL_0022: ldloc.0
IL_0023: ldloc.1
IL_0024: bne.un.s IL_0006
IL_0026: ret
} // End of method System.Void DotnetExample.Program::remove_eventFields(DotnetExample.VoidCallback)
// eventFields事件定义了+= addon 和-= removeon两个函数
.event class DotnetExample.VoidCallback eventFields
{
.addon default void DotnetExample.Program::add_eventFields (class DotnetExample.VoidCallback 'value')
.removeon default void DotnetExample.Program::remove_eventFields (class DotnetExample.VoidCallback 'value')
} // End of property DotnetExample.VoidCallback DotnetExample.Program::eventFields
} // End of class DotnetExample.Program
委托与事件的区别
根据上两节的分析,总结委托与事件的区别。
相同点
- 事件本质也是委托
不同点
- 事件访问权限都是private的,要触发事件调用只能在类的内部。
- 在外部,事件只能通过+=或-=进行回调函数的添加或删除