可能重复:
C#: Difference between ‘ += anEvent’ and ‘ += new EventHandler(anEvent)’
订阅活动有两种基本方式:
SomeEvent += new EventHandler<ArgType> (MyHandlerMethod);
SomeEvent += MyHandlerMethod;
有什么区别,我应该何时选择其中一个?
编辑:如果它是相同的,那么为什么VS默认为长版本,使代码混乱?这对我来说毫无意义。
答案 0 :(得分:40)
由于我原来的答案似乎存在争议,我决定做一些测试,包括查看生成的代码和监控性能。
首先,这是我们的测试床,一个带有委托的类和另一个使用它的类:
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。我本来错了。但那不是整个故事。这可能是我在这里偏离主题但我认为在谈论事件和代表时包含这一点非常重要:
创建和比较不同的代表并不便宜。
当我写这篇文章时,我认为第一种语法能够将方法组转换为委托,但事实证明它只是一种转换。但是当你实际保存委托时,情况就完全不同了。如果我们将此添加到消费者:
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
使用已保存的委托与创建新委托时,差异几乎是 20%。
现在显然不是每个程序都会在如此短的时间内添加和删除这么多代理,但是如果你正在编写库类 - 可能以你无法预测的方式使用的类 - 那么你真的想要如果您需要添加并删除事件,请记住这一点(我已经编写了大量代码来执行此操作,个人而言)。
因此,结论是,将SomeEvent += new EventHandler(NamedMethod)
编写为与SomeEvent += NamedMethod
相同的内容。但是,如果您计划稍后删除该事件处理程序,您确实应该保存代理。尽管Delegate
类有一些特殊情况代码允许你从你添加的代理中删除一个与引用不同的委托,但它必须做一些非常重要的工作才能解决这个问题。
如果您不打算保存委托,那么它没有任何区别 - 编译器最终还是会创建一个新的委托。
答案 1 :(得分:25)
从编程角度来看,没有区别,它们彼此等同。编译器几乎可以完成你在幕后第二行的第一行所做的工作。所以我总是选择第二种方法(更少的代码)。
重新:您的修改
可能是因为他们觉得向开发人员展示正确的做事方式而不是快捷方式会更好。你的猜测和我一样好:)。
答案 2 :(得分:9)
第二种形式是在c#的后续版本中引入的语法糖。第一行可以在每个版本中使用
答案 3 :(得分:7)
没有区别。在.NET 2.0之前,每个变量赋值必须是精确类型,编译器然后没有多少推断。为了解决这个问题,VS 2003在函数名称周围发出new EventHandler
。这只是我的猜测。因为..
我现在在VS 2008 textBox1.KeyDown += (KeyEventHandler)textBox1_KeyDown
尝试了一些东西,它也有效。令我困惑的是他们为什么选择new EventHandler(checkBox1_CheckStateChanged)
,而不是(EventHandler)checkBox1_CheckStateChanged
。但...
因为我的盒子中没有VS 2003了,所以我不能确定是否可以在VS 2003上使用转换方法。但是,我在尝试使用函数名时删除了new EventHandler
VS 2003(.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 += button1_Click;
之间存在差异,但遗憾的是我看不出IL级别的任何差异: - (
http://blogs.msdn.com/abhinaba/archive/2005/08/26/456437.aspx
答案 4 :(得分:2)
没有区别,第一个在定义中更具体。