Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

events

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.

like image 349
mafu Avatar asked May 01 '10 12:05

mafu


People also ask

Is an EventHandler a delegate?

The EventHandler delegate is a predefined delegate that specifically represents an event handler method for an event that does not generate data. If your event does generate data, you must use the generic EventHandler<TEventArgs> delegate class.

How does EventHandler work?

Each key node has a handler that receives key events when the key has focus. The handler responds to the key-pressed and key-released events for the Enter key by changing the color of the key on the screen. The event is then consumed so that the keyboard node, which is the parent node, does not receive the event.

How do you declare an EventHandler?

Use the EventHandler delegate for all events that do not include event data. Use the EventHandler<TEventArgs> delegate for events that include data about the event. These delegates have no return type value and take two parameters (an object for the source of the event, and an object for event data).

What is generally the return type of event handlers?

By default most event handlers return void, however, it is possible for handlers to return values.


2 Answers

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.

like image 157
Aaronaught Avatar answered Oct 07 '22 10:10

Aaronaught


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 :)

like image 23
James Avatar answered Oct 07 '22 09:10

James