Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If events are implemented as delegates in .NET, what is the point of the .event IL section?

I've seen some very good questions on Stack Overflow concerning delegates, events, and the .NET implementation of these two features. One question in particular, "How do C# Events work behind the scenes?", produced a great answer that explains some subtle points very well.

The answer to the above question makes this point:

When you declare a field-like event ... the compiler generates the methods and a private field (of the same type as the delegate). Within the class, when you refer to ElementAddedEvent you're referring to the field. Outside the class, you're referring to the field

An MSDN article linked from the same question ("Field-like events") adds:

The notion of raising an event is precisely equivalent to invoking the delegate represented by the event — thus, there are no special language constructs for raising events.

Wanting to examine further, I built a test project in order to view the IL that an event and a delegate are compiled to:

public class TestClass
{
    public EventHandler handler;
    public event EventHandler FooEvent;

    public TestClass()
    { }
}

I expected the delegate field handler and the event FooEvent to compile to roughly the same IL code, with some additional methods to wrap access to the compiler-generated FooEvent field. But the IL generated wasn't quite what I expected:

.class public auto ansi beforefieldinit TestClass
    extends [mscorlib]System.Object
{
    .event [mscorlib]System.EventHandler FooEvent
    {
        .addon instance void TestClass::add_FooEvent(class [mscorlib]System.EventHandler)
        .removeon instance void TestClass::remove_FooEvent(class [mscorlib]System.EventHandler)
    }

    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
        // Constructor IL hidden
    }

    .field private class [mscorlib]System.EventHandler FooEvent
    .field public class [mscorlib]System.EventHandler handler
}

Since events are nothing more than delegates with compiler-generated add and remove methods, I didn't expect to see events treated as anything more than that in IL. But the add and remove methods are defined in a section that begins .event, not .method as normal methods are.

My ultimate questions are: if events are implemented simply as delegates with accessor methods, what is the point of having a .event IL section? Couldn't they be implemented in IL without this by using .method sections? Is .event equivalent to .method?

like image 210
Chris Avatar asked Oct 20 '08 20:10

Chris


People also ask

Why do we need events when we have delegates?

Yes, Events! This is a simplified explanation on why we use events even though we can already use delegates: To provide encapsulation and not exposing business logic. To prevent Team Client from clearing all assign methods to delegates (You cannot do that for events):

What are events How are events and delegates associated in the .NET framework?

NET Framework is based on having an event delegate that connects an event with its handler. To raise an event, two elements are needed: A delegate that identifies the method that provides the response to the event. Optionally, a class that holds the event data, if the event provides data.

Why We Use delegates and events in C#?

Delegates allow methods to be passed as parameters. Delegates can be used to define callback methods. Delegates can be chained together; for example, multiple methods can be called on a single event. Methods don't have to match the delegate type exactly.

What is the relationship between events and delegates in C#?

Event is a notification raised by an object to signal the occurrence of an action. Delegate is associated with the event to hold a reference of a method to be called when the event is raised.


1 Answers

I'm not sure that is surprising... compare to the same for properties vs fields (since properties before the same function as events: encapsulation via accessors):

.field public string Foo // public field
.property instance string Bar // public property
{
    .get instance string MyType::get_Bar()
    .set instance void MyType::set_Bar(string)
}

Also - events do not mention anything about fields; they only define the accessors (add/remove). The delegate backer is an implementation detail; it just so happens that field-like-events declare a field as a backing member - in the same way that auto-implemented-properties declare a field as a backing member. Other implementations are possible (and very common, especially in forms etc).

Other common implementations:

Sparse-events (Controls, etc) - EventHandlerList (or similar):

// only one instance field no matter how many events;
// very useful if we expect most events to be unsubscribed
private EventHandlerList events = new EventHandlerList();
protected EventHandlerList Events {
    get { return events; } // usually lazy
}

// this code repeated per event
private static readonly object FooEvent = new object();
public event EventHandler Foo
{
    add { Events.AddHandler(FooEvent, value); }
    remove { Events.RemoveHandler(FooEvent, value); }
}
protected virtual void OnFoo()
{
    EventHandler handler = Events[FooEvent] as EventHandler;
    if (handler != null) handler(this, EventArgs.Empty);
}

(the above is pretty-much the backbone of win-forms events)

Facade (although this confuses the "sender" a little; some intermediary code is often helpful):

private Bar wrappedObject; // via ctor
public event EventHandler SomeEvent
{
    add { wrappedObject.SomeOtherEvent += value; }
    remove { wrappedObject.SomeOtherEvent -= value; }
}

(the above can also be used to effectively rename an event)

like image 55
Marc Gravell Avatar answered Nov 10 '22 03:11

Marc Gravell