+ = new EventHandler(Method)vs + = Method

maf*_*afu 73 c# events

可能重复:
C#:'+ = anEvent'和'+ = new EventHandler(anEvent)'之间的区别

订阅活动有两种基本方式:

SomeEvent += new EventHandler<ArgType> (MyHandlerMethod);
SomeEvent += MyHandlerMethod;
Run Code Online (Sandbox Code Playgroud)

有什么区别,什么时候我应该选择一个而不是另一个?

编辑:如果它是相同的,那么为什么VS默认为长版本,使代码混乱?这对我来说毫无意义.

Aar*_*ght 43

由于我的原始答案似乎存在争议,我决定做一些测试,包括查看生成的代码监控性能.

首先,这是我们的测试床,一个带有委托的类和另一个使用它的类:

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++;
    }
}
Run Code Online (Sandbox Code Playgroud)

首先要看的是生成的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 
}
Run Code Online (Sandbox Code Playgroud)

事实证明,是的,这些确实产生了相同的IL.我本来错了.但这不是整个故事.这可能是我在这里偏离主题但我认为在谈论事件和代表时包含这一点非常重要:

创建和比较不同的代表并不便宜.

当我写这篇文章时,我认为第一种语法能够将方法组转换为委托,但事实证明它只是一种转换.但是当你真正保存代表时它完全不同.如果我们将此添加到消费者:

class Counter
{
    EventHandler savedEvent;

    public Counter()
    {
        savedEvent = CountEvent;
    }

    public void CountSaved()
    {
        producer.EventRaised += savedEvent;
        producer.Raise();
        producer.EventRaised -= savedEvent;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到,这与其他两个特性在性能方面有很大不同:

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;
}
Run Code Online (Sandbox Code Playgroud)

结果一直回归类似于:

Count: 00:00:02.4742007
CountWithNew: 00:00:02.4272702
CountSaved: 00:00:01.9810367
Run Code Online (Sandbox Code Playgroud)

使用已保存的委托与创建新委托时,这几乎相差20%.

现在显然不是每个程序都会在如此短的时间内添加和删除这么多代理,但如果你正在编写库类 - 可能以你无法预测的方式使用的类 - 那么你真的想保留这个如果您需要添加和删除事件(我已经编写了很多代码来执行此操作,请亲自处理).

因此,结论就是将SomeEvent += new EventHandler(NamedMethod)编译编译成同样的东西SomeEvent += NamedMethod.但是如果您打算稍后删除该事件处理程序,那么您真的应该保存该委托.尽管Delegate该类具有一些特殊情况代码,允许您从您添加的代理中删除引用不同的委托,但它必须执行一些非常重要的工作来完成此操作.

如果您不打算保存委托,那么它没有任何区别 - 编译器最终会创建一个新的委托.

  • 只是为了确保我写了一个小测试并查看了IL.2个表单生成__identical__ IL代码. (2认同)
  • 操作节省20%可能需要100毫秒?来吧. (2认同)

Jam*_*mes 25

编程视角没有区别,它们彼此等同.编译器几乎可以完成你在幕后第二行的第一行所做的工作.所以我总是选择第二种方法(更少的代码).

回复:你的编辑

可能是因为他们觉得向开发人员展示正确的做事方式而不是快捷方式会更好.你的猜测和我的一样好:)


kni*_*ttl 9

第二种形式是在c#的后续版本中引入的语法糖.第一行将适用于每个版本


Mic*_*uen 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支持我new EventHandler在.NET 2.0编译器之前的记忆,它是一个强制性的

[编辑]

以下文章深入介绍了订阅/取消订阅事件,声称在button1.Click += new EventHandler(button1_Click);和之间存在差异button1.Click += button1_Click;,但遗憾的是我看不出IL级别的任何差异:-(

http://blogs.msdn.com/abhinaba/archive/2005/08/26/456437.aspx