C# +;=新EventHandler(方法)与+;=方法
可能重复:C# +;=新EventHandler(方法)与+;=方法,c#,events,C#,Events,可能重复: 订阅活动有两种基本方式: SomeEvent += new EventHandler<ArgType> (MyHandlerMethod); SomeEvent += MyHandlerMethod; SomeEvent+=neweventhandler(MyHandlerMethod); SomeEvent+=MyHandlerMethod; 有什么区别,什么时候我应该选择一个而不是另一个 编辑:如果是相同的,那么为什么VS默认为长版本,导致代码混乱?这对我来说毫
订阅活动有两种基本方式:
SomeEvent += new EventHandler<ArgType> (MyHandlerMethod);
SomeEvent += MyHandlerMethod;
SomeEvent+=neweventhandler(MyHandlerMethod);
SomeEvent+=MyHandlerMethod;
有什么区别,什么时候我应该选择一个而不是另一个
编辑:如果是相同的,那么为什么VS默认为长版本,导致代码混乱?这对我来说毫无意义。没有区别,第一个定义更具体。从编程角度看没有区别,它们彼此是等价的。编译器将完成您在第一行所做的工作,而第二行则在幕后完成。所以我总是选择第二种方法(更少的代码) Re:您的编辑
可能是因为他们觉得向开发人员展示正确的做事方式比捷径更好。你的猜测和我的一样好:)没有区别。在.NET2.0之前,每个变量赋值都必须是精确的类型,然后编译器就不会推断太多。为了解决这个问题,VS 2003围绕函数名发出
neweventhandler
。那只是我的猜测。因为
我现在在VS2008中尝试了一些东西,textBox1.KeyDown+=(KeyEventHandler)textBox1\u KeyDown
,同样有效。这让我很困惑,为什么他们选择新的事件处理程序(checkBox1\u CheckStateChanged)
,而不是(EventHandler)checkBox1\u CheckStateChanged
。但是
由于我的盒子里不再有VS2003了,我无法确定演员阵容是否也能在VS2003上发挥作用。但是最近,当我使用VS 2003(.NET 1.1)时,我尝试删除函数名上的new EventHandler
,考虑到为什么需要实例化(new EventHandler
)一个函数,委托只是引擎盖下的函数指针,但它不起作用
直到.NET2.0之后,C#编译器才开始尽可能多地推断
本文支持我对.NET2.0编译器之前的neweventhandler
的记忆,这是强制的
[编辑]
下面的文章深入讨论了订阅/取消订阅事件,声称button1.Click+=neweventhandler(button1\u-Click)之间存在差异代码>和按钮1。单击+=按钮1\u单击代码>,但遗憾的是,我看不到IL水平有任何差异:-(
第二种形式是在以后的c#版本中引入的语法糖。第一行在每个版本中都会起作用,尽管由于对我的原始答案似乎存在一些争议,我决定做一些测试,包括查看生成的代码和监控性能
首先,这是我们的测试平台,一个带有委托的类和另一个使用它的类:
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;
}
当使用保存的代理与创建新代理时,这几乎是20%的差别
很明显,不是每个程序都会在这么短的时间内添加和删除这么多委托,但是如果您正在编写库类——这些类可能以您无法预测的方式使用——那么如果您需要添加和删除事件,那么您真的需要记住这一差异(我个人编写了很多这样做的代码)
因此,本文的结论是,编写SomeEvent+=neweventhandler(NamedMethod)
编译成与SomeEvent+=NamedMethod
相同的东西。但是如果您计划以后删除该事件处理程序,您确实应该保存该委托。即使delegate
类有一些特殊情况代码,允许您从添加的委托中删除引用不同的委托,它也会被删除必须做大量的工作才能成功
如果你不打算保存委托,那也没什么区别——编译器最终还是会创建一个新委托。不,长版本不是一个解决办法,而是完整的语法。另一个只是编译器重写为长版本的简写。@Henk Holterman:我删除了我的答案,然后对答案进行了大量编辑。我很抱歉我不太确定那时候它不是强制的,所以在我尝试写我能记住的东西和取消删除之前,我试着用谷歌搜索一篇文章的第一篇,这篇文章支持我当时所记得的东西。长格式是强制的,但我不能断定它是否是解决编译器显式类型分配兼容性的方法,我才刚刚开始学习ildasm now:-)我不知道实例指令在IL中做了什么我现在正在检查长格式在.NET 1.1中是必要的,在.C#2引入短格式之前。随着匿名方法的出现,lambdas.Code生成器变得更加混乱(使用长限定名而不是使用指令)。它们的目标是易于生成和避免错误,而不是可读性。我特别提到的是代码段,它不是自动代码生成器使用的。我也是。长格式不太可能产生歧义。@Henk,我明白了,是的。我想这是有意义的。:)为了确保我编写了一个小测试并查看了
Count: 00:00:02.4742007
CountWithNew: 00:00:02.4272702
CountSaved: 00:00:01.9810367