Possible Duplicate:
C#: Difference between ‘ += anEvent’ and ‘ += new EventHandler(anEvent)’
订阅事件有两种基本方式:
SomeEvent += new EventHandler<ArgType> (MyHandlerMethod);
SomeEvent += MyHandlerMethod;
有什么区别,什么时候我应该选择一个而不是另一个?
编辑:如果是一样的,那么为什么 VS 默认为长版本,使代码困惑?这对我来说毫无意义。
由于我的原始答案似乎存在一些争议,因此我决定进行一些测试,包括查看生成的代码和监控性能。
首先,这是我们的测试平台,一个带有委托(delegate)的类和另一个使用它的类:
class EventProducer
{
public void Raise()
{
var handler = EventRaised;
if (handler != null)
handler(this, EventArgs.Empty);
}
public event EventHandler EventRaised;
}
class Counter
{
long count = 0;
EventProducer producer = new EventProducer();
public void Count()
{
producer.EventRaised += CountEvent;
producer.Raise();
producer.EventRaised -= CountEvent;
}
public void CountWithNew()
{
producer.EventRaised += new EventHandler(CountEvent);
producer.Raise();
producer.EventRaised -= new EventHandler(CountEvent);
}
private void CountEvent(object sender, EventArgs e)
{
count++;
}
}
首先要做的是查看生成的 IL:
.method public hidebysig instance void Count() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0006: ldarg.0
L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler)
L_0017: ldarg.0
L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise()
L_0022: ldarg.0
L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0028: ldarg.0
L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler)
L_0039: ret
}
.method public hidebysig instance void CountWithNew() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0006: ldarg.0
L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler)
L_0017: ldarg.0
L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise()
L_0022: ldarg.0
L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0028: ldarg.0
L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler)
L_0039: ret
}
事实证明,是的,它们确实生成了相同的 IL。原来我错了。但这不是全部。这可能是我跑题了,但我认为在谈论事件和代表时包括这一点很重要:
创建和比较不同的委托(delegate)并不便宜。
写这个的时候,我以为第一种语法可以把方法组强制转换为委托(delegate),结果发现只是一个转换而已。但当您实际保存委托(delegate)时,情况就完全不同了。如果我们将其添加到消费者:
class Counter
{
EventHandler savedEvent;
public Counter()
{
savedEvent = CountEvent;
}
public void CountSaved()
{
producer.EventRaised += savedEvent;
producer.Raise();
producer.EventRaised -= savedEvent;
}
}
您可以看到,在性能方面,这与其他两个具有非常不同的特征:
static void Main(string[] args)
{
const int TestIterations = 10000000;
TimeSpan countTime = TestCounter(c => c.Count());
Console.WriteLine("Count: {0}", countTime);
TimeSpan countWithNewTime = TestCounter(c => c.CountWithNew());
Console.WriteLine("CountWithNew: {0}", countWithNewTime);
TimeSpan countSavedTime = TestCounter(c => c.CountSaved());
Console.WriteLine("CountSaved: {0}", countSavedTime);
Console.ReadLine();
}
static TimeSpan TestCounter(Action<Counter> action, int iterations)
{
var counter = new Counter();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < TestIterations; i++)
action(counter);
sw.Stop();
return sw.Elapsed;
}
返回的结果始终类似于:
Count: 00:00:02.4742007
CountWithNew: 00:00:02.4272702
CountSaved: 00:00:01.9810367
使用保存的委托(delegate)与创建新委托(delegate)时,这几乎有 20% 的差异。
现在显然不是每个程序都会在这么短的时间内添加和删除这么多委托(delegate),但是如果您正在编写库类 - 可能以您无法预测的方式使用的类 - 那么您真的想要如果您需要添加和删除事件(我个人编写了很多代码来执行此操作),请牢记这一差异。
因此得出的结论是,编写 SomeEvent += new EventHandler(NamedMethod)
编译成与 SomeEvent += NamedMethod
相同的东西。但是,如果您打算稍后删除该事件处理程序,您确实应该保存委托(delegate)。尽管 Delegate
类有一些特殊情况的代码,允许您从添加的委托(delegate)中删除引用不同的委托(delegate),但它必须做大量的工作才能实现这一点。
如果您不打算保存委托(delegate),那么这没有任何区别 - 编译器最终还是会创建一个新的委托(delegate)。