C#: Difference Between ' += Anevent' and ' += New Eventhandler(Anevent)'

C#: Difference between ' += anEvent' and ' += new EventHandler(anEvent)'

There is no difference. In your first example, the compiler will automatically infer the delegate you would like to instantiate. In the second example, you explicitly define the delegate.

Delegate inference was added in C# 2.0. So for C# 1.0 projects, second example was your only option. For 2.0 projects, the first example using inference is what I would prefer to use and see in the codebase - since it is more concise.

Attaching Eventhandler with New Handler vs Directly assigning it

In C# 2.0 and above, they are identical. In C# 1.2 (the one that shipped with .NET 1.1), only the first syntax (with new) compiles ;-p

The second syntax saves key presses, but VS intellisense will typically suggest the first. Ultimately, it makes very little difference. I generally use the second syntax in code-samples online, simply because it avoids going over the (narrow) column width!

Understanding events and event handlers in C#

To understand event handlers, you need to understand delegates. In C#, you can think of a delegate as a pointer (or a reference) to a method. This is useful because the pointer can be passed around as a value.

The central concept of a delegate is its signature, or shape. That is (1) the return type and (2) the input arguments. For example, if we create a delegate void MyDelegate(object sender, EventArgs e), it can only point to methods which return void, and take an object and EventArgs. Kind of like a square hole and a square peg. So we say these methods have the same signature, or shape, as the delegate.

So knowing how to create a reference to a method, let's think about the purpose of events: we want to cause some code to be executed when something happens elsewhere in the system - or "handle the event". To do this, we create specific methods for the code we want to be executed. The glue between the event and the methods to be executed are the delegates. The event must internally store a "list" of pointers to the methods to call when the event is raised.* Of course, to be able to call a method, we need to know what arguments to pass to it! We use the delegate as the "contract" between the event and all the specific methods that will be called.

So the default EventHandler (and many like it) represents a specific shape of method (again, void/object-EventArgs). When you declare an event, you are saying which shape of method (EventHandler) that event will invoke, by specifying a delegate:

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;

//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
//Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

//To raise the event within a method.
SomethingHappened("bar");

(*This is the key to events in .NET and peels away the "magic" - an event is really, under the covers, just a list of methods of the same "shape". The list is stored where the event lives. When the event is "raised", it's really just "go through this list of methods and call each one, using these values as the parameters". Assigning an event handler is just a prettier, easier way of adding your method to this list of methods to be called).

+= new EventHandler(Method) vs += Method

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.

An object reference is required for the non-static field, method, or property error on new EventHandler

If this code snippet appears within a static method then there is no 'this' pointer, and you cannot access the window_Closing and window_Closed methods.

Try making window_Closed and window_Closing static, to see if it compiles - but be warned that they will also not have a 'this' pointer.

What is probably the 'real' fix is of course is to make the method non-static, and in the current static method create an instance of the class then call that non-static method.

(Note: not compiled and tried this snippet)

class SomeClass
{
static void CurrentFunc()
{
var someClass = new SomeClass();
someClass.NewFunc();
}

private void NewFunc()
{
Window window = FindWindow(windowId);
if (window == null)
{
window = new Window();
window.Closing += new System.ComponentModel.CancelEventHandler(window_Closing);
window.Closed += new EventHandler(window_Closed);
_winDict.Add(windowId, window);
}
window.Owner = Application.Current.MainWindow;
window.Title = title;
window.Content = guc;
window.SizeToContent = SizeToContent.WidthAndHeight;
window.ResizeMode = ResizeMode.NoResize;
window.ShowInTaskbar = false;
}

private void window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
throw new NotImplementedException();
}

private void window_Closed(object sender, EventArgs e)
{
throw new NotImplementedException();
}
}

Difference between wiring events with and without new

I did this

static void Hook1()
{
someEvent += new EventHandler( Program_someEvent );
}

static void Hook2()
{
someEvent += Program_someEvent;
}

And then ran ildasm over the code.

The generated MSIL was exactly the same.

So to answer your question, yes they are the same thing.

The compiler is just inferring that you want someEvent += new EventHandler( Program_someEvent );

-- You can see it creating the new EventHandler object in both cases in the MSIL

C# why shall I use new keyword when subscribing for an event?

The verbose way works in all versions of C#, the short way only in C# 2 and later. So I see no reason to use the long way nowadays.

There are some situations where you still need to use new DelegateType(methodGroup), but event subscribing isn't one of them. These situations usually involve generic type inference or method overloading.

Unsubscribing will work either way since it is based on value equality, not referential equality. If I recall correctly both the implicit conversion from a method group and the explicit new get translated to the same IL code. The implicit conversion is just syntax sugar.



Related Topics



Leave a reply



Submit