Program Tip

+ = new EventHandler (Method) vs + = Method

programtip 2020. 10. 27. 23:08
반응형

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

    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 
}

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.


There is no difference from a programming perspective, they are each others equivalent. The compiler will pretty much do what you have done on the first line with the second line behind the scenes. So I would always opt for the second approach (less code).

Re: Your Edit

Probably because they feel its better to show developers the proper way of doing things rather than shortcuts. Your guess is as good as mine :)


the second form is syntactic sugar introduced in later versions of c#. the first line will work in every version though


There's no difference. Prior to .NET 2.0, every variable assignments must be of exact type, compilers then didn't infer much. So as to make a work-around, VS 2003 emit new EventHandler around the function name. That's just my guess. Because..

I tried something now in VS 2008, textBox1.KeyDown += (KeyEventHandler)textBox1_KeyDown, that also work. It puzzles me why they choose new EventHandler(checkBox1_CheckStateChanged), rather than (EventHandler)checkBox1_CheckStateChanged then. But...

as I don't have VS 2003 in my box anymore, I can't pin down if the casting approach could also work on VS 2003. But afaict, I tried removing new EventHandler on function name when I used VS 2003 (.NET 1.1), deeming why the need to instantiate (new EventHandler) a function, delegates are just function pointer under the hood, but it doesn't work.

It's only from .NET 2.0 onwards that C# compiler started to infer as much as possible.

This article http://blueonionsoftware.com/blog.aspx?p=aed2ae46-7548-4e5f-83c6-95e00c6f3649 supported my memory of new EventHandler prior to .NET 2.0 compilers, it was a compulsary

[EDIT]

The following article goes in depth about subscribing/unsubcribing events, purporting there's a difference between button1.Click += new EventHandler(button1_Click); and button1.Click += button1_Click;, but sadly I can't see any difference in IL level though :-(

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


There is no difference, the first one just is more specific in its definition.

참고URL : https://stackoverflow.com/questions/2749868/new-eventhandlermethod-vs-method

반응형