学习C#的事件模式时Delegate和event这二个概念比较难理解,因为这二个都被设计成关键字,想进一步查看其定义和实现就没有下文了,不过再难也得硬着头皮弄明白,我以前好长时间都是似懂非懂的状态,后来在学习了观察者设计模式和IL后利用这二者的知识来理解就比较容易。下面我就用观察者设计模式和IL来分析一下事件模式中Delegate和event这二个关键字。
首先,我们来了解一下delegate究竟是什么东西,先来看一下下面的程序(为了演示,代码尽量简略)。
Using System;
public class Test
{
public delegatevoid aaa();
public static void Main(String[]args) {
aaaa=new aaa(show);
a();
}
public static voidshow()
{
Console.WriteLine("hi");
}
}
以上代码用csc.exe编译后再用ILDASM反编译成文本文件:
.class public auto ansibeforefieldinit Test extends [mscorlib]System.Object
{
.class auto ansisealed nested publicaaa extends[mscorlib]System.MulticastDelegate
{
.method public hidebysig specialnamertspecialname instance void .ctor(object 'object', native int'method') runtime managed
{
} // end of method aaa::.ctor
.method public hidebysig newslotvirtual instance void Invoke() runtime managed
{
} //end of method aaa::Invoke
.method public hidebysig newslotvirtual instance class[mscorlib]System.IAsyncResult BeginInvoke(class [mscorlib]System.AsyncCallback callback,
object 'object')runtime managed
{
} // end of method aaa::BeginInvoke
.method public hidebysig newslotvirtual instance void EndInvoke(class [mscorlib]System.IAsyncResultresult) runtime managed
{
} // end of method aaa::EndInvoke
} // end of class aaa
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
//
.maxstack 2
.locals init (class Test/aaa V_0)
IL_0000: nop
IL_0001: ldnull
//将show方法的指针推送到计算堆栈上
IL_0002: ldftn void Test::show()
IL_0008: newobj instance voidTest/aaa::.ctor(object,
native int)
IL_000d: stloc.0
IL_000e: ldloc.0
IL_000f: callvirt instance voidTest/aaa::Invoke()
IL_0014: nop
IL_0015: ret
} // end of method Test::Main
.method public hidebysig static void show() cil managed
{
//
.maxstack 8
IL_0000: nop
IL_0001: ldstr "hi"
IL_0006: call void[mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Test::show
.method public hidebysig specialnamertspecialname
instance void .ctor() cil managed
{
//
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void[mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Test::.ctor
} // end of class Test
从反编译出来IL指令中可以看到delegate关键字修饰的aaa由编译器转换为一个继承自System.MulticastDelegate的类,然后在main方法中以方法show的指针作参数构造一个aaa的实例,后面再通过该实例调用Invoke方法,相信至此对delegate关键字应该明白其作用了。
要了解事件模式的话,光是了解delegate还不够,我们还得看看event是什么玩艺:
using System;
public class Test {
public static voidMain(String[] args) {
EventTest et = newEventTest();
et.OnPropertyChanged+=show;
et.Age=2;
}
public static void show()
{
Console.WriteLine("event trigged");
}
}
public class EventTest
{
public delegate voidPropertyChangedHandler();
public eventPropertyChangedHandler OnPropertyChanged;
private int age;
public int Age
{
get { return age; }
set
{
age = value;
if(OnPropertyChanged!=null)
{
OnPropertyChanged();
}
}
}
}
学过MVVM模式的朋友对以上代码是否有似曾相识的感觉,用csc.exe编译后再用ILDASM反编译成文本文件:
.class public auto ansi beforefieldinit Test extends[mscorlib]System.Object
{
.method public hidebysig staticvoid Main(string[] args) cil managed
{
.entrypoint
//
.maxstack 3
.locals init (class EventTestV_0)
IL_0000: nop
IL_0001: newobj instance voidEventTest::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldnull
IL_0009: ldftn void Test::show()
IL_000f: newobj instance void EventTest/PropertyChangedHandler::.ctor(object,native int)
IL_0014: callvirt instance void EventTest::add_OnPropertyChanged(classEventTest/PropertyChangedHandler)
IL_0019: nop
IL_001a: ldloc.0
IL_001b: ldc.i4.2
IL_001c: callvirt instance void EventTest::set_Age(int32)
IL_0021: nop
IL_0022: ret
} // end of method Test::Main
.method public hidebysig staticvoid show() cil managed
{
//
.maxstack 8
IL_0000: nop
IL_0001: ldstr "event trigged"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Test::show
.method public hidebysigspecialname rtspecialname instancevoid .ctor() cil managed
{
//
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Test::.ctor
} // end of class Test
.class public auto ansi beforefieldinit EventTest extends [mscorlib]System.Object
{
.class auto ansi sealed nestedpublic PropertyChangedHandler extends[mscorlib]System.MulticastDelegate
{
.method public hidebysigspecialname rtspecialname instancevoid .ctor(object 'object', native int 'method') runtime managed
{
} // end of methodPropertyChangedHandler::.ctor
.method public hidebysig newslotvirtual instance void Invoke() runtime managed
{
} // end of methodPropertyChangedHandler::Invoke
.method public hidebysig newslotvirtual instance class[mscorlib]System.IAsyncResult
BeginInvoke(class[mscorlib]System.AsyncCallback callback, object 'object') runtime managed
{
} // end of methodPropertyChangedHandler::BeginInvoke
.method public hidebysig newslotvirtual instance void EndInvoke(class [mscorlib]System.IAsyncResultresult) runtime managed
{
} // end of methodPropertyChangedHandler::EndInvoke
} // end of classPropertyChangedHandler
.field private classEventTest/PropertyChangedHandler OnPropertyChanged
.field private int32 age
.method public hidebysigspecialname instance void add_OnPropertyChanged(classEventTest/PropertyChangedHandler 'value') cil managed
{
//
.maxstack 3
.locals init (classEventTest/PropertyChangedHandler V_0,
classEventTest/PropertyChangedHandler V_1,
classEventTest/PropertyChangedHandler V_2,
bool V_3)
IL_0000: ldarg.0
IL_0001: ldfld class EventTest/PropertyChangedHandler EventTest::OnPropertyChanged
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: stloc.1
IL_0009: ldloc.1
IL_000a: ldarg.1
IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class[mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0010: castclass EventTest/PropertyChangedHandler
IL_0015: stloc.2
IL_0016: ldarg.0
IL_0017: ldflda class EventTest/PropertyChangedHandler EventTest::OnPropertyChanged
IL_001c: ldloc.2
IL_001d: ldloc.1
IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<classEventTest/PropertyChangedHandler>(!!0&, !!0, !!0)
IL_0023: stloc.0
IL_0024: ldloc.0
IL_0025: ldloc.1
IL_0026: ceq
IL_0028: ldc.i4.0
IL_0029: ceq
IL_002b: stloc.3
IL_002c: ldloc.3
IL_002d: brtrue.s IL_0007
IL_002f: ret
} // end of methodEventTest::add_OnPropertyChanged
.method public hidebysigspecialname instance void
remove_OnPropertyChanged(classEventTest/PropertyChangedHandler 'value') cil managed
{
//
.maxstack 3
.locals init (classEventTest/PropertyChangedHandler V_0,
classEventTest/PropertyChangedHandler V_1,
classEventTest/PropertyChangedHandler V_2,
bool V_3)
IL_0000: ldarg.0
IL_0001: ldfld class EventTest/PropertyChangedHandler EventTest::OnPropertyChanged
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: stloc.1
IL_0009: ldloc.1
IL_000a: ldarg.1
IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0010: castclass EventTest/PropertyChangedHandler
IL_0015: stloc.2
IL_0016: ldarg.0
IL_0017: ldflda class EventTest/PropertyChangedHandler EventTest::OnPropertyChanged
IL_001c: ldloc.2
IL_001d: ldloc.1
IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<classEventTest/PropertyChangedHandler>(!!0, !!0, !!0)
IL_0023: stloc.0
IL_0024: ldloc.0
IL_0025: ldloc.1
IL_0026: ceq
IL_0028: ldc.i4.0
IL_0029: ceq
IL_002b: stloc.3
IL_002c: ldloc.3
IL_002d: brtrue.s IL_0007
IL_002f: ret
} // end of methodEventTest::remove_OnPropertyChanged
.method public hidebysigspecialname instance int32 get_Age() cil managed
{
//
.maxstack 1
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 EventTest::age
IL_0007: stloc.0
IL_0008: br.s IL_000a
IL_000a: ldloc.0
IL_000b: ret
} // end of methodEventTest::get_Age
.method public hidebysigspecialname instance void set_Age(int32'value') cil managed
{
//
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.1
IL_0003: stfld int32 EventTest::age
IL_0008: ldarg.0
IL_0009: ldfld class EventTest/PropertyChangedHandlerEventTest::OnPropertyChanged
IL_000e: ldnull
IL_000f: ceq
IL_0011: stloc.0
IL_0012: ldloc.0
IL_0013: brtrue.s IL_0023
IL_0015: nop
IL_0016: ldarg.0
IL_0017: ldfld class EventTest/PropertyChangedHandler EventTest::OnPropertyChanged
IL_001c: callvirt instance void EventTest/PropertyChangedHandler::Invoke()
IL_0021: nop
IL_0022: nop
IL_0023: ret
} // end of methodEventTest::set_Age
.method public hidebysigspecialname rtspecialname instance void .ctor() cil managed
{
//
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of methodEventTest::.ctor
.eventEventTest/PropertyChangedHandler OnPropertyChanged
{
.addon instancevoid EventTest::add_OnPropertyChanged(class EventTest/PropertyChangedHandler)
.removeoninstance void EventTest::remove_OnPropertyChanged(classEventTest/PropertyChangedHandler)
} // end of eventEventTest::OnPropertyChanged
.property instance int32 Age()
{
.get instance int32EventTest::get_Age()
.set instance voidEventTest::set_Age(int32)
} // end of propertyEventTest::Age
} // end of class EventTest
用event声明事件后编译器在编译时给类中加了二个方法:
add_OnPropertyChanged(class EventTest/PropertyChangedHandler 'value')、
remove _OnPropertyChanged(class EventTest/PropertyChangedHandler 'value')。
在这二个方法中有二个调用:
System.Delegate::Combine(class [mscorlib]System.Delegate, class[mscorlib]System.Delegate)、
System.Delegate::Remove(class[mscorlib]System.Delegate, class[mscorlib]System.Delegate),
查一下MSDN这二个调用是干什么的:
Combine方法将两个委托的调用列表连接在一起,Remove方法从一个委托的调用列表中移除另一个委托的最后一个调用列表。
委托里有个调用列表!
挖到这里,学过观察者模式的朋友是否有似曾相识的感觉:观察者模式中被观察者也有一个订阅者列表,有Add和Remove方法。事件模式不就是观察者模式吗?,不过在C#中用delegate来实现观察者模式中的更新状态的接口和订阅者列表,用event关键字实现的订阅者的添加和删除。
理解了事件模式和观察者模式的关系,对MVVM模式中的Notification类也就不难理解了。