+= new EventHandler(Method) vs += Method
C#EventsC# Problem Overview
> 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.
C# Solutions
Solution 1 - C#
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.
Solution 2 - C#
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 :)
Solution 3 - C#
the second form is syntactic sugar introduced in later versions of c#. the first line will work in every version though
Solution 4 - C#
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
Solution 5 - C#
There is no difference, the first one just is more specific in its definition.