How Do C# Events Work Behind the Scenes

How do C# Events work behind the scenes?

I've written this up in a fair amount of detail in an article, but here's the summary, assuming you're reasonably happy with delegates themselves:

  • An event is just an "add" method and a "remove" method, in the same way that a property is really just a "get" method and a "set" method. (In fact, the CLI allows a "raise/fire" method as well, but C# never generates this.) Metadata describes the event with references to the methods.
  • When you declare a field-like event (like your ElementAddedEvent) 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.
  • When anyone subscribes to an event (with the += operator) that calls the add method. When they unsubscribe (with the -= operator) that calls the remove.
  • For field-like events, there's some synchronization but otherwise the add/remove just call Delegate.Combine/Remove to change the value of the auto-generated field. Both of these operations assign to the backing field - remember that delegates are immutable. In other words, the autogenerated code is very much like this:

    // Backing field
    // The underscores just make it simpler to see what's going on here.
    // In the rest of your source code for this class, if you refer to
    // ElementAddedEvent, you're really referring to this field.
    private EventHandler<EventArgs> __ElementAddedEvent;

    // Actual event
    public EventHandler<EventArgs> ElementAddedEvent
    {
    add
    {
    lock(this)
    {
    // Equivalent to __ElementAddedEvent += value;
    __ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value);
    }
    }
    remove
    {
    lock(this)
    {
    // Equivalent to __ElementAddedEvent -= value;
    __ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value);
    }
    }
    }
  • The initial value of the generated field in your case is null - and it will always become null again if all subscribers are removed, as that is the behaviour of Delegate.Remove.

  • If you want a "no-op" handler to subscribe to your event, so as to avoid the nullity check, you can do:

    public EventHandler<EventArgs> ElementAddedEvent = delegate {};

    The delegate {} is just an anonymous method which doesn't care about its parameters and does nothing.

If there's anything that's still unclear, please ask and I'll try to help!

Can someone explain what is happening behind the scenes?

Event handler lists are delegates, and delegates are immutable -- like strings. So you do copy the delegate, and the second event handler gets "added to" the 2nd delegate, not the first.

You can find out more about delegates at http://www.c-sharpcorner.com/uploadfile/Ashush/delegates-in-C-Sharp/

Good luck!

.NET's Event Mechanism

I hope I'm understanding your question correctly. I think we're talking about two things.

First - how events work in C#
Second - how a WinForms application written in C# knows when you've clicked a button.

Events in C# are their own distinct thing. You can write a console app, create your own event, listen to it, fire it, respond to it, etc... and it all just works. You subscribe to an event by called Add() and you unsubscribe by calling Remove(). The event itself keeps track of what methods are listening to it and when it's raised, calls all of those methods.

Jon Skeet explains it much better:
How do C# Events work behind the scenes?

But these events are just C# code. Related to, but distinct from the Win32 messages you're mentioning. In a Winforms application, when the user clicks a button, how does the application know about it? We can look using the debugger (turn off the 'My Code' https://msdn.microsoft.com/en-us/library/dn457346.aspx option) and set a breakpoint in the click event, you'll be able to see what is going on.

Callstack

So in Windows.Forms.Controls.ControlNativeWindow there is a WndProc method that takes in a System.Windows.Forms.Message m.

Right before that is a 'debuggableCallback' method. That mirrors what you'd expect from a Win32API app.

Source here:
http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeWindow.cs,ad40308c5b6490dd

/// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.DebuggableCallback"]/*' />
/// <devdoc>
/// Window message callback method. Control arrives here when a window
/// message is sent to this Window. This method packages the window message
/// in a Message object and invokes the wndProc() method. A WM_NCDESTROY
/// message automatically causes the releaseHandle() method to be called.
/// </devdoc>
/// <internalonly/>
private IntPtr DebuggableCallback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) {

// Note: if you change this code be sure to change the
// corresponding code in Callback above!

Message m = Message.Create(hWnd, msg, wparam, lparam);

try {
if (weakThisPtr.IsAlive && weakThisPtr.Target != null) {
WndProc(ref m);
}
else {
DefWndProc(ref m);
}
}
finally {
if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false);
if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true);
}

return m.Result;
}

So, ultimately, if you're running on Windows, it's driven by the same Win32 API messages you'd expect. It's just that the System.Windows.Forms classes are written to encapsulate most of it from us.

c# classes Subscribing to events of each other

Here's the code you need:

class A
{
B b;
public event EventHandler eventA;

void OnEventA()
{
var ea = this.eventA;
if (ea != null)
{
ea(this, new EventArgs());
}
}

public A()
{
this.b = new B(this);
this.b.eventB += DoSomethingElse;
}

private void DoSomethingElse(object sender, EventArgs e)
{
}
}

class B
{
A a;
public B(A a)
{
this.a = a;
this.a.eventA += DoSomething;
}
public event EventHandler eventB;

void OnEventB()
{
var eb = this.eventB;
if (eb != null)
{
eb(this, new EventArgs());
}
}

private void DoSomething(object sender, EventArgs e)
{
}
}

What is an event on a lower level?

There's two parts to this question - how does a CLR (aka C#) event compare to a Windows message, and how does a Button implement a CLR event.

On the one hand - yes, a Button's Click event is a wrapper over the message loop and WM_ messages:

protected override void WndProc(ref Message m) {
switch (m.Msg) {
case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND:
if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.BN_CLICKED) {
Debug.Assert(!GetStyle(ControlStyles.UserPaint), "Shouldn't get BN_CLICKED when UserPaint");
if (!ValidationCancelled) {
OnClick(EventArgs.Empty);
}
}
break;
case NativeMethods.WM_ERASEBKGND:
DefWndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}

On the other hand, a CLR event has nothing to do with message loops and WM_ messages - it only appears to because you're looking at it in context of WinForms, which chooses to expose WM messages as C# events.

A C# event is nothing more than a callback routine - you register a callback, and the implementing class calls it when something interesting happens.

What is meaning of `event = method` in C#

Note that only field-like events can be used in this way:

SomeEvent = SomeMethod;

SomeEvent += SomeMethod; and SomeEvent -= SomeMethod; on the other hand can be used with all events, and they call the add and remove accessors of SomeEvent respectively.

Here's what a field-like event is, from the language spec:

Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event [...] must not explicitly include event_accessor_declarations. Such an event can be used in any context that permits a field. The field contains a delegate which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains null.

In general, a field like event does not have explicit add and remove accessors.

Notice how they have a delegate-typed field storing the list of event handlers. SomeEvent = SomeMethod; basically sets the field to a delegate whose invocation list contains only SomeMethod. This means that all the previous handlers in the list are discarded. If you are wondering how a method name can be converted to a delegate, see method group conversions.

The add and remove accessors of field-like events are automatically generated:

When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock on the containing object for an instance event, or the type object for a static event.

The spec doesn't say exactly how, so that's up to the implementation to decide. On SharpLab, we can see that one implementation would be:

private EventHandler m_SomeEvent;

private event EventHandler SomeEvent
{
[CompilerGenerated]
add
{
EventHandler eventHandler = this.SomeEvent;
while (true)
{
EventHandler eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value);
eventHandler = Interlocked.CompareExchange(ref this.SomeEvent, value2, eventHandler2);
if ((object)eventHandler == eventHandler2)
{
break;
}
}
}
[CompilerGenerated]
remove
{
EventHandler eventHandler = this.SomeEvent;
while (true)
{
EventHandler eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2, value);
eventHandler = Interlocked.CompareExchange(ref this.SomeEvent, value2, eventHandler2);
if ((object)eventHandler == eventHandler2)
{
break;
}
}
}
}

Note the field m_SomeEvent, and the calls to Delegate.Remove and Delegate.Combine - those are what adds the new handler to the list of handlers, and removes a handler from the list of handlers.

So can we use += instead of = or is it bad to use += when you only have to subscribe event to single method?

It's not bad to use += when you subscribe to only one method. In fact, I would recommend you to use +=. You may say "I only want to subscribe to a single method" now, but you never know when that's going to change :)

Events and Delegates

Probably this answer will help you.He has explained it in good detail:-

Events

How EventHandler += method is implemented?

is it a special compiler thingy that is exclusive for that class?

It is a special compiler thingy. It's not bound to EventHandler though, but events in general. You can declare an event with any kind of delegate.

Behind the scenes, it just calls Delegate.Combine with a bit of synchronization to handle race conditions:

This code:

public event EventHandler Test;

public void Hello()
{
Test += Program_Test
}

Will compile into:

private EventHandler Test;

public event EventHandler Test
{
add
{
EventHandler eventHandler = this.Test;
EventHandler comparand;
do
{
comparand = eventHandler;
eventHandler = Interlocked.CompareExchange<EventHandler>(ref this.Test, (EventHandler) Delegate.Combine((Delegate) comparand, (Delegate) value), comparand);
}
while (eventHandler != comparand);
}
remove
{
EventHandler eventHandler = this.Test;
EventHandler comparand;
do
{
comparand = eventHandler;
eventHandler = Interlocked.CompareExchange<EventHandler>(ref this.Test, (EventHandler) Delegate.Remove((Delegate) comparand, (Delegate) value), comparand);
}
while (eventHandler != comparand);
}
}


Related Topics



Leave a reply



Submit