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