+ =新的EventHandler(方法)vs + =方法
可能重复:
C#:“+ = anEvent”和“+ = new EventHandler(anEvent)”之间的区别
订阅事件有两种基本的方法:
SomeEvent += new EventHandler<ArgType> (MyHandlerMethod); SomeEvent += MyHandlerMethod;
有什么区别,我应该什么时候select一个呢?
编辑:如果它是相同的,那么为什么VS默认为长版本,混乱的代码? 对我来说这毫无意义。
由于我原来的答案似乎有些争执,所以我决定做一些testing,包括查看生成的代码和监视性能。
首先,这里是我们的testing平台,一个代表和另一个类的消费者:
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 }
所以事实certificate,是的,这些产生了相同的IL。 我原来错了。 但这不是全部 。 这可能是我在这里讨论的话题,但我认为在谈论事件和代表时,包括这一点很重要:
创build和比较不同的代表并不便宜。
当我写这篇文章的时候,我在想第一个语法能够把方法组作为委托,但事实certificate,这只是一个转换。 但是,当你真的保存委托时,它是完全不同的。 如果我们将其添加到消费者:
class Counter { EventHandler savedEvent; public Counter() { savedEvent = CountEvent; } public void CountSaved() { producer.EventRaised += savedEvent; producer.Raise(); producer.EventRaised -= savedEvent; } }
你可以看到,从另外两个angular度来看,这具有非常不同的性能performance:
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
使用已保存的委托与创build新委托时相差近20% 。
现在显然不是每一个程序都会在这么短的时间内增加和删除这么多的代表,但是如果你正在编写图书馆类 – 那些可能以你无法预测的方式使用的类 – 那么你真的想保留这个不同的是,如果你需要添加和删除事件(我已经写了很多代码,亲自做这个)。
所以这个结论是,编写SomeEvent += new EventHandler(NamedMethod)
编译为SomeEvent += NamedMethod
相同的事情。 但是,如果您打算稍后移除该事件处理程序,则确实应该保存该委托 。 尽pipeDelegate
类有一些特殊的代码,可以让你删除一个与你添加的Delegate
,但它必须做一些不重要的工作来解决这个问题。
如果你不打算保存委托,那么没有什么区别 – 编译器最终会创build一个新的委托。
从编程的angular度来看没有什么区别,它们彼此是等价的。 编译器几乎可以完成你在第一行的第二行后面所做的事情。 所以我会一直select第二种方法(less代码)。
Re:你的编辑
可能是因为他们觉得向开发者展示正确的做事方式而不是快捷方式更好。 你的猜测和我一样好:)
第二种forms是在c#的更高版本中引入的语法糖。 第一行将在每个版本中工作
没有区别。 在.NET 2.0之前,每个variables赋值必须是精确的types,编译器则没有太多的推断。 为了解决这个问题,VS 2003在函数名称周围放出了new EventHandler
。 这只是我的猜测。 因为..
我现在在VS 2008中尝试了一些东西, textBox1.KeyDown += (KeyEventHandler)textBox1_KeyDown
,也可以。 它让我困惑,为什么他们selectnew EventHandler(checkBox1_CheckStateChanged)
,而不是(EventHandler)checkBox1_CheckStateChanged
。 但…
因为我的箱子里没有VS 2003,所以如果在VS2003上也可以使用这种方法,那么我就不能放弃了。但是在我使用VS 2003的时候,我尝试在函数名上删除new EventHandler
(.NET 1.1 ),认为为什么需要实例化( new EventHandler
)的一个函数,委托只是函数指针的底层,但是不起作用。
只有从.NET 2.0开始,C#编译器才开始尽可能地推断。
本文http://blueonionsoftware.com/blog.aspx?p=aed2ae46-7548-4e5f-83c6-95e00c6f3649在;.NET 2.0编译器之前支持我对new EventHandler
记忆,这是一个强制性的
[编辑]
下面的文章会深入地介绍订阅/取消订阅事件,声称button1.Click += new EventHandler(button1_Click);
之间有区别。 button1.Click += new EventHandler(button1_Click);
和button1.Click += button1_Click;
,但不幸的是,我不能看到任何IL水平的差异,虽然:-(
http://blogs.msdn.com/abhinaba/archive/2005/08/26/456437.aspx
没有什么区别,第一个定义更具体。