Understanding events and event handlers in C#

后端 未结 12 1489
野性不改
野性不改 2020-11-22 04:06

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 vo         


        
相关标签:
12条回答
  • 2020-11-22 04:49

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

    0 讨论(0)
  • 2020-11-22 04:49

    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. However, there are many cases to think about where you don't want the rest of the application to have knowledge of myFactory or to call myFactory.GetInstance() directly.

    An obvious one is if you want to be able to replace myFactory.GetInstance() into myOfflineFakeFactory.GetInstance() from one central place (aka factory method pattern).

    Factory method pattern

    So, if you have a TheOtherClass class and it needs to use the myFactory.GetInstance(), this is how the code will look like without delegates (you'll need to let TheOtherClass know about the type of your myFactory):

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

    If you'd use delegates, you don't have to expose the type of my factory:

    TheOtherClass toc;
    //...
    Action factoryMethod = myFactory.GetInstance;
    toc.SetFactoryMethod(factoryMethod);
    
    
    class TheOtherClass
    {
       public void SetFactoryMethod(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, see below) reference multiple methods (called MulticastDelegate). This means that when you call the delegate, multiple explicitly-attached methods will be executed. An object reference can always only reference to one object.

    The restrictions for a MulticastDelegate are that the (method/delegate) signature should not have any return value (void) and the keywords out and ref is not used in the signature. Obviously, you can't call two methods that return a number and expect them to return the same number. Once the signature complies, the delegate is automatically a MulticastDelegate.

    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,WPF,UWP So on)

    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 methods. 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.

    Java

    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 keep a list of all objects implementing this interface and can call their 'certain method we can call' whenever we get clicked.

    0 讨论(0)
  • 2020-11-22 04:49

    Just to add to the existing great answers here - building on the code in the accepted one, which uses a delegate void MyEventHandler(string foo)...

    Because the compiler knows the delegate type of the SomethingHappened event, this:

    myObj.SomethingHappened += HandleSomethingHappened;
    

    Is totally equivalent to:

    myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
    

    And handlers can also be unregistered with -= like this:

    // -= removes the handler from the event's list of "listeners":
    myObj.SomethingHappened -= HandleSomethingHappened;
    

    For completeness' sake, raising the event can be done like this, only in the class that owns the event:

    //Firing the event is done by simply providing the arguments to the event:
    var handler = SomethingHappened; // thread-local copy of the event
    if (handler != null) // the event is null if there are no listeners!
    {
        handler("Hi there!");
    }
    

    The thread-local copy of the handler is needed to make sure the invocation is thread-safe - otherwise a thread could go and unregister the last handler for the event immediately after we checked if it was null, and we would have a "fun" NullReferenceException there.


    C# 6 introduced a nice short hand for this pattern. It uses the null propagation operator.

    SomethingHappened?.Invoke("Hi there!");
    
    0 讨论(0)
  • 2020-11-22 04:52

    My understanding of the events is;

    Delegate:

    A variable to hold reference to method / methods to be executed. This makes it possible to pass around methods like a variable.

    Steps for creating and calling the event:

    1. The event is an instance of a delegate

    2. Since an event is an instance of a delegate, then we have to first define the delegate.

    3. Assign the method / methods to be executed when the event is fired (Calling the delegate)

    4. Fire the event (Call the delegate)

    Example:

    using System;
    
    namespace test{
        class MyTestApp{
            //The Event Handler declaration
            public delegate void EventHandler();
    
            //The Event declaration
            public event EventHandler MyHandler;
    
            //The method to call
            public void Hello(){
                Console.WriteLine("Hello World of events!");
            }
    
            public static void Main(){
                MyTestApp TestApp = new MyTestApp();
    
                //Assign the method to be called when the event is fired
                TestApp.MyHandler = new EventHandler(TestApp.Hello);
    
                //Firing the event
                if (TestApp.MyHandler != null){
                    TestApp.MyHandler();
                }
            }
    
        }   
    
    }
    
    0 讨论(0)
  • 2020-11-22 04:53
    //This delegate can be used to point to methods
    //which return void and take a string.
    public delegate void MyDelegate(string foo);
    
    //This event can cause any method which conforms
    //to MyEventHandler to be called.
    public event MyDelegate MyEvent;
    
    //Here is some code I want to be executed
    //when SomethingHappened fires.
    void MyEventHandler(string foo)
    {
        //Do some stuff
    }
    
    //I am creating a delegate (pointer) to HandleSomethingHappened
    //and adding it to SomethingHappened's list of "Event Handlers".
    myObj.MyEvent += new MyDelegate (MyEventHandler);
    
    0 讨论(0)
  • 2020-11-22 04:53

    Great technical answers in the post! I have nothing technically to add to that.

    One of the main reasons why new features appear in languages and software in general is marketing or company politics! :-) This must not be under estimated!

    I think this applies to certain extend to delegates and events too! i find them useful and add value to the C# language, but on the other hand the Java language decided not to use them! they decided that whatever you are solving with delegates you can already solve with existing features of the language i.e. interfaces e.g.

    Now around 2001 Microsoft released the .NET framework and the C# language as a competitor solution to Java, so it was good to have NEW FEATURES that Java doesn't have.

    0 讨论(0)
提交回复
热议问题