+ = new EventHandler(Method)vs + = Method [英] += new EventHandler(Method) vs += Method

查看:100
本文介绍了+ = new EventHandler(Method)vs + = Method的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


可能重复:

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);
}

公共事件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。程序/ 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 / EventPro ducer :: add_EventRaised(class [mscorlib] System.EventHandler)
L_0017:ldarg.0
L_0018:ldfld class DelegateTest.Program / EventProducer DelegateTest.Program / Counter :: producer
L_001d:callvirt实例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;
}
}

你可以看到, / em>不同的特征,性能方面,从其他两个:

  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();
秒表sw =新秒表();
sw.Start(); (int i = 0; i< TestIterations; i ++)
action(counter);

sw.Stop();
return sw.Elapsed;
}

结果一直回到类似于:

 计数:00:00:02.4742007 
CountWithNew:00:00:02.4272702
CountSaved:00:00:01.9810367

使用保存的代理与创建新代理时,差不多是20%



现在显然,每个程序都不会在这么短的时间内添加和删除这么多代理,但如果你正在编写图书馆类 - 可能以无法预测的方式使用 - 那么如果您需要添加并删除事件(而且我已经写了很多代码,这样做,亲自) )



所以得出结论是,写 SomeEvent + = new EventHandler(NamedMethod)编译成相同的事情只是 SomeEvent + = NamedMethod 。但是,如果您打算稍后再次删除该事件处理程序,那么您真的应该保存代理。即使 Delegate 类有一些特殊情况代码,允许您从添加的代理中删除一个不同的委托,它必须做一个非常小的数量的努力把它拉下来。



如果你不打算保存代表,那没有任何区别 - 编译器最终会创建一个新的委托。 p>

Possible Duplicate:
C#: Difference between ‘ += anEvent’ and ‘ += new EventHandler(anEvent)’

There are two basic ways to subscribe to an event:

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

What is the difference, and when should I chose one over the other?

Edit: If it is the same, then why does VS default to the long version, cluttering the code? That makes no sense at all to me.

解决方案

Since there seemed to be some dispute over my original answer, I decided to do a few tests, including looking at the generated code and monitoring the performance.

First of all, here's our test bed, a class with a delegate and another class to consume it:

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++;
    }
}

First thing to do is look at the generated 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 
}

So it turns out that, yes, these do generate identical IL. I was wrong originally. But that's not the whole story. It may be that I'm going off-topic here but I think that it's important to include this when talking about events and delegates:

Creating and comparing different delegates is not cheap.

When I wrote this, I was thinking that the first syntax was able to cast the method group as a delegate, but it turns out that it's just a conversion. But it's completely different when you actually save the delegate. If we add this to the consumer:

class Counter
{
    EventHandler savedEvent;

    public Counter()
    {
        savedEvent = CountEvent;
    }

    public void CountSaved()
    {
        producer.EventRaised += savedEvent;
        producer.Raise();
        producer.EventRaised -= savedEvent;
    }
}

You can see that this has very different characteristics, performance-wise, from the other two:

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;
}

The results consistently come back as something similar to:

Count: 00:00:02.4742007
CountWithNew: 00:00:02.4272702
CountSaved: 00:00:01.9810367

That's nearly a 20% difference when using a saved delegate vs. creating a new one.

Now obviously not every program is going to be adding and removing this many delegates in such a small amount of time, but if you're writing library classes - classes that might be used in ways you cannot predict - then you really want to keep this difference in mind if you ever need to add and remove events (and I've written a lot of code that does this, personally).

So the conclusion of this is, writing SomeEvent += new EventHandler(NamedMethod) compiles to the same thing as just SomeEvent += NamedMethod. But if you plan to remove that event handler later, you really should save the delegate. Even though the Delegate class has some special-case code that allows you to remove a referentially-different delegate from the one you added, it has to do a non-trivial amount of work to pull this off.

If you're not going to save the delegate, then it makes no difference - the compiler ends up creating a new delegate anyway.

这篇关于+ = new EventHandler(Method)vs + = Method的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆