+ =新的EventHandler(方法)vs + =方法

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

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

SomeEvent += new EventHandler<ArgType> (MyHandlerMethod);
SomeEvent += MyHandlerMethod;

有什么区别,我应该什么时候选择一个呢?

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


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

首先,这里是我们的测试平台,一个包含委托和另一个类的消费类:

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类有一些特殊的代码,可以让你删除一个与你添加的Delegate ,但它必须做一些不重要的工作来解决这个问题。

如果你不想保存委托,那么它没有什么区别 - 编译器最终会创建一个新的委托。


从编程的角度来看没有什么不同,它们彼此是等价的。 编译器几乎可以完成你在第一行和第二行后面完成的工作。 所以我会一直选择第二种方法(少代码)。

Re:你的编辑

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


第二种形式是在更高版本的c#中引入的语法糖。 第一行将在每个版本中工作

链接地址: http://www.djcxy.com/p/51489.html

上一篇: += new EventHandler(Method) vs += Method

下一篇: C# pattern to prevent an event handler hooked twice