Raising and handling events

Methods are often described as actions that an object can do. For example, a List class can add an item to itself or clear itself.

Events are often described as actions that happen to an object. For example, in a user interface, Button has a Click event, click being something that happens to a button.

Another way of thinking of events is a way of exchanging messages between two objects.

Calling methods using delegates

You have already seen the most common way to call or execute a method: use the dot syntax to access the method using its name. For example, Console.WriteLine tells the Console type to write out the message to the console window or terminal.

The other way to call or execute a method is to use a delegate. If you have used languages that support function pointers, then think of a delegate as being a type-safe method pointer. In other words, a delegate is the memory address of a method that matches the same signature as the delegate so that it can be safely called.

For example, imagine there is a method that must have a string passed as its only parameter and it returns an int:

    public int MethodIWantToCall(string input) 
    { 
      return input.Length; // it doesn't matter what this does 
    } 

I could call this method directly like this:

    int answer = p1.MethodIWantToCall("Frog"); 

Alternatively, I could define a delegate with a matching signature to call the method indirectly. Notice that the names of parameters do not have to match. Only the types of parameters and return values must match:

    delegate int DelegateWithMatchingSignature(string s); 

Now, I can create an instance of the delegate, point it at the method, and finally call the delegate (which calls the method!):

    var d = new DelegateWithMatchingSignature(p1.MethodIWantToCall); 
    int answer2 = d("Frog"); 

You are probably thinking, "What's the point of that?" Well, it provides flexibility.

We could use delegates to create a queue of methods that need to be called in order. Delegates have built-in support for asynchronous operations that run on a different thread for better performance. Most importantly, delegates allow us to create events.

Note

Delegates and events are one of the most advanced features of C# and can take a few attempts to understand, so don't worry if you're feeling lost!

Defining events

Microsoft has two predefined delegates for use as events. They look like this:

    public delegate void EventHandler(object sender, EventArgs e); 
    public delegate void EventHandler<TEventArgs>(object sender,
    TEventArgs e); 

Tip

Good Practice

When you want to define an event in your own type, you should use one of these two predefined delegates.

Add the following code to the Person class. The code defines an event named Shout. It also defines a field to store AngerLevel and a method named Poke. Each time a person is poked, their anger level increments. Once their anger level reaches three, they raise the Shout event, but only if the event delegate is pointing at a method defined somewhere else in code, that is, not null:

    // event 
    public event EventHandler Shout; 
 
    // field 
    public int AngerLevel; 
 
    // method 
    public void Poke() 
    { 
      AngerLevel++; 
      if (AngerLevel >= 3) 
      { 
        // if something is listening... 
        if (Shout != null) 
        { 
          // ...then raise the event 
          Shout(this, EventArgs.Empty); 
        } 
      } 
    } 

Note

Checking if an object is null before calling one of its methods is very common. C# allows these statements to be simplified like this: Shout?.Invoke(this, EventArgs.Empty);

In Visual Studio 2017, in the Main method, start typing the following code to assign an event handler:

    harry.Shout += 

Notice the IntelliSense that appears when you enter the += operator, as shown in the following screenshot:

Defining events

Press Tab. You will now see a preview of what Visual Studio would like to do for you, as shown in the following screenshot:

Defining events

Press Enter to accept the name of the method.

Visual Studio 2017 inserts a method that correctly matches the signature of the event delegate. This method will be automatically called when the event is raised.

Scroll down to find the method Visual Studio 2017 created for you and delete the statement that throws NotImplementedException.

In Visual Studio Code, you must write the method and assign its name yourself. The method should look like this. The name can be anything, but Harry_Shout is sensible:

    private static void Harry_Shout(object sender, EventArgs e) 
    { 
    } 

In Visual Studio Code, in the Main method, add the following statement to assign the method to the event:

    harry.Shout += Harry_Shout; 

In both Visual Studio 2017 and Visual Studio Code, add statements to the Harry_Shout method to get a reference to the Person object and output some information about them, as shown in the following code:

    private static void Harry_Shout(object sender, EventArgs e) 
    { 
      Person p = (Person)sender; 
      WriteLine($"{p.Name} is this angry: {p.AngerLevel}."); 
    } 

Back in the Main method, add the following statements to call the Poke method four times, after assigning the method to the Shout event:

    harry.Shout += harry_Shout; 
    harry.Poke(); 
    harry.Poke(); 
    harry.Poke(); 
    harry.Poke();

Run the application. Note that Harry only gets angry enough to shout once he's been poked at least three times:

Harry is this angry: 3.
Harry is this angry: 4.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.195.215