Tuesday, May 3, 2011

understanding events and event handlers in C#

I understand the purpose of events, especially within the context of creating user interfaces. I think this is the prototype for creating an event.

public void EventName(object sender, EventArgs e);

but I don't understand event handlers, specifically what they do, why they're needed, and to create one. Could someone please enlighten me?

From stackoverflow
  • This is an old useful link I found about delegates and events. This is for C#2.0.

    .NET Delegates: A C# Bedtime Story

  • That is actually the declaration for an event handler - a method that will get called when an event is fired. To create an event, you'd write something like this:

    public class Foo
    {
        public event EventHandler MyEvent;
    }
    

    And then you can subscribe to the event like this:

    Foo foo = new Foo();
    foo.MyEvent += new EventHandler(this.OnMyEvent);
    

    With OnMyEvent() defined like this:

    private void OnMyEvent(object sender, EventArgs e)
    {
        MessageBox.Show("MyEvent fired!");
    }
    

    Whenever Foo fires off MyEvent, then your OnMyEvent handler will be called.

    You don't always have to use an instance of EventArgs as the second parameter. If you want to include additional information, you can use a class derived from EventArgs (EventArgs is the base by convention). For example, if you look at some of the events defined on Control in WinForms, or FrameworkElement in WPF, you can see examples of events that pass additional information to the event handlers.

  • 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 the return type and input parameters. 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 the methods to call when the event is raised.* Since we of course need to know what arguments to pass to a method to be able to call it, we use the delegate as the "contract" between the event and all the methods.

    So the default EventHandler (and many like it) represents a specific type of method (again, void/object-EventArgs). When you declare an event, you include which type 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 methods which conform
    //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);
    

    (*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).

    Levi Campbell : I really like how you explained the concepts. Thank you.
    Joel in Gö : And now can anyone explain why the event is called EventHandler?? Of all the confusing naming conventions, this is the worst...
    Rex M : @Joel in Go the event is not called EventHandler - EventHandler is the contract the event must have with anyone who communicates with it. It's like "string MyString" - the string is declaring the type. event MyEventHandler TheEvent is declaring that anyone who interacts with this event must conform to the MyEventHandler contract. The Handler convention is because the contract primarily describes how to handle the event.
    alchemical : How is the event fired?
    Rex M : @LuftMensch from inside the class which owns the event, the syntax `EventName(delegateArg1, delegateArg2);` is valid. That invokes the event like a method, which kind of kicks off a chain, calling each handler assigned to that event with those arguments.
    Joel in Gö : @Rex M : thank you for the first coherent explanation for "MyEventHandler" that I have ever seen :)
  • C# knows two terms, delegate and event. Let's start with the first one.

    Delegate

    A delegate is a reference to a method. Just like you can create a reference to an instance:

    MyClass instance = myFactory.GetInstance();
    

    You can use a delegate to create an reference to a method:

    Action myMethod = myFactory.GetInstance;
    

    Now that you have this reference to a method, you can call the method via the reference:

    MyClass instance = myMethod();
    

    But why would you? You can also just call myFactory.GetInstance() directly. In this case you can. But suppose that you don't want to call this method youself, but want to give it other classes to call. Those other classes don't know about the type of myFactory. In that case, you'll need to let the other classes know about the type of your myFactory:

    TheOtherClass toc;
    //...
    toc.SetFactory(myFactory);
    
    
    class TheOtherClass
    {
       public void SetFactory(MyFactory factory)
       {
          // set here
       }
    
    }
    

    Thanks to delegates, you don't have to expose the type of my factory:

    TheOtherClass toc;
    //...
    Action factoryMethod = myFactory.SetFactory;
    toc.SetFactoryMethod(factoryMethod);
    
    
    class TheOtherClass
    {
       public void SetFactory(Action factoryMethod)
       {
          // set here
       }
    
    }
    

    Thus, you can give a delegate to some other class to use, without exposing your type to them. The only thing you're exposing is the signature of your method (how many parameters you have and such).

    "Signature of my method", where did I hear that before? O yes, interfaces!!! interfaces describe the signature of a whole class. Think of delegates as describing the signature of only one method!

    Another large difference between an interface and a delegate, is that when you're writing your class, you don't have to say to C# "this method implements that type of delegate". With interfaces you do need to say "this class implements that type of an interface".

    Further, a delegate reference can (with some restrictions) reference multiple methods. An object reference can always only reference to one object.

    Event

    Events are just properties (like the get;set; properties to instance fields) which expose subscription to the delegate from other objects. These properties, however don't support get;set;. Instead they support add;remove;

    So you can have:

        Action myField;
    
        public event Action MyProperty
        {
            add { myField += value; }
            remove { myField -= value; }
        }
    

    Usage in UI (WinForms)

    So, now we know that a delegate is a reference to a method and that we can have an event to let the world know that they can give us their methods to be referenced from our delegate, and we are a UI button, then: we can ask anyone who is interested in whether I was clicked, to register their method with us (via the event we exposed). We can use all those methods that were given to us, and reference them by our delegate. And then, we'll wait and wait.... until a user comes and clicks on that button, then we'll have enough reason to invoke the delegate. And because the delegate references all those methods given to us, all those methods will be invoked. We don't know what those methods do, nor we know which class implements those method. All we do care about is that someone was interested in us being clicked, and gave us a reference to a method that complied with our desired signature.

    Heaving said this, languages like Java don't have delegates. They use interfaces instead. The way they do that is to ask anyone who is interested in 'us being clicked', to implement a certain interface (with a certain method we can call), then give us the whole instance that implements the interface. We can that keep a list of all objects implementing this interface, and can call their 'certain method we can call' whenever we get clicked.

  • Here is a code example which may help

    using System;
    
    using System.Collections.Generic;
    
    using System.Text;
    
    
    
    namespace Event_Example
    
    {
    
     //First we have to define a delegate that acts as a signature for the
    
     //function that is ultimately called when the event is triggered.
    
     //You will notice that the second parameter is of MyEventArgs type.
    
     //This object will contain information about the triggered event.
    
     public delegate void MyEventHandler(object source, MyEventArgs e);
    
    
    
     //This is a class which describes the event to the class that recieves it.
    
     //An EventArgs class must always derive from System.EventArgs.
    
     public class MyEventArgs : EventArgs
    
     {
    
      private string EventInfo;
    
      public MyEventArgs(string Text)
    
      {
    
       EventInfo = Text;
    
      }
    
      public string GetInfo()
    
      {
    
       return EventInfo;
    
      }
    
     }
    
    
    
     //This next class is the one which contains an event and triggers it
    
     //once an action is performed. For example, lets trigger this event
    
     //once a variable is incremented over a particular value. Notice the
    
     //event uses the MyEventHandler delegate to create a signature
    
     //for the called function.
    
     public class MyClass
    
     {
    
      public event MyEventHandler OnMaximum;
    
      private int i;
    
      private int Maximum = 10;
    
      public int MyValue
    
      {
    
       get
    
       {
    
        return i;
    
       }
    
       set
    
       {
    
        if(value <= Maximum)
    
        {
    
         i = value;
    
        }
    
        else
    
        {
    
         //To make sure we only trigger the event if a handler is present
    
         //we check the event to make sure it's not null.
    
         if(OnMaximum != null)
    
         {
    
          OnMaximum(this, new MyEventArgs("You've entered " +
    
           value.ToString() +
    
           ", but the maximum is " +
    
           Maximum.ToString()));
    
         }
    
        }
    
       }
    
      }
    
     }
    
    
    
     class Program
    
     {
    
      //This is the actual method that will be assigned to the event handler
    
      //within the above class. This is where we perform an action once the
    
      //event has been triggered.
    
      static void MaximumReached(object source, MyEventArgs e)
    
      {
    
       Console.WriteLine(e.GetInfo());
    
      }
    
    
    
      static void Main(string[] args)
    
      {
    
       //Now lets test the event contained in the above class.
    
       MyClass MyObject = new MyClass();
    
       MyObject.OnMaximum += new MyEventHandler(MaximumReached);
    
    
    
       for(int x = 0; x <= 15; x++)
    
       {
    
        MyObject.MyValue = x;
    
       }
    
    
    
       Console.ReadLine();
    
      }
    
     }
    
    }
    
  • Here is a link to an excellent article on C# Events.

0 comments:

Post a Comment