Events in C # (event)

event model

Five components of the event model

  • event source (class object) (some books call it event publisher)
  • Event member (the member of the event owner) (the event member is the event itself. The event does not take the initiative, but only occurs under the trigger of the internal logic of the event owner.)
  • Event subscriber (class object) (some books call it event subscriber)
  • event handler (member of the responder of the event) (process the event according to the obtained event parameters / information)
  • Event subscription (delegate type)

Take a chestnut: "the referee fired and the athlete began to run."

In the above example, the event owner is the referee, the event member is the shooter, the event responder is the athlete, and the event processing is to start running.

As for event subscription, it is not a specific object or member. Its function is to associate the event with the event processor. It represents the relationship between the event owner and the event responder. For example, when the referee shoots, the athletes on his competition site will start, but other athletes hundreds of miles away will not start, and the spectators watching the competition will not start, Because they have no contact with the referee, that is, they do not subscribe to the referee's events.

In addition, event subscription can also restrict what event parameters event members can pass to the event handler, and what signature and return value type the event handler should have (method signature consists of method name and parameter list, excluding return value type). We can't casually use any event processor to deal with events, just like when the referee shoots, the athlete should start rather than sit down and have a cup of tea.

5 actions from occurrence to response

① The event owner owns an event → ② the event responder subscribes to the event → ③ the event owner triggers the event → ④ the event responder will be notified in turn (in the order of subscription) → ⑤ the event responder processes the event according to the obtained event parameters

Three combinations of event model components

1. Event owners and event responders belong to different categories:

namespace TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            Form myForm = new Form(); //Event owner: myForm
            Test myTest = new Test(myForm); //Event responder: myTest
            myForm.ShowDialog(); //Display Form 
        }
    }

    class Test
    {
        private Form form;
        public Test(Form form)
        {
            if (form != null)
            {
                this.form = form;
                this.form.Click += FormClicked; //Mouse click event
                //Event: Click; Event handler: FormClicked; Event subscription:+=
            }
        }
        //Event handler: (automatically generated by compiler)
        private void FormClicked(object sender, EventArgs e)
        {
            form.Text = DateTime.Now.ToString(); //Use the current time as the title of the form
        }
    }
}

//Event: click the window with the mouse;
//Who owns this event: the form myForm is clicked by the mouse;
//Who responded to this event: myTest, when the form is clicked, it needs to deal with it;
//What to do: add the current time to the form.

2. The event owner and the event responder are the same object

namespace TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            MyForm form = new MyForm(); //Both the event owner and the event responder are form s
            form.Click += form.FormClicked; //Event Click, event handler, FormClicked, event subscription+=
            form.ShowDialog();
            //If it is written here:
            //Form form = new Form();
            //form.Click += form.FormClicked;
            //The implementation part of 'FormClicked' cannot be generated automatically by the compiler
            //Because Form is a class provided by the system, we can't change it at will,
            //So you can't use the compiler to automatically generate methods for event handlers as before
            //This requires us to write a class to inherit (class myform: form)
        }
        
    }
    class MyForm : Form
    {
        //Event handler
        internal void FormClicked(object sender, EventArgs e)
        {
            this.Text = "Hello World!";
        }
    }
}

3. The event owner is a field of the event responder, or the event responder is a field of the event owner

This is a common method in WinForm programming. For example, in a form, there is a text box and button. Now click the button to display the text "Hello World" on the text box.

The events occurred are: mouse click; The event owner is a Button, which is clicked by the mouse and is a field member of the form object; The event responder is a form object; Event handling: the form object displays the text "Hello World" in its field member TextBox.

//Text boxes and buttons are generated by visual programming operations, and then event handlers are added to the buttons:
private void buttonClicked(object sender, EventArgs e)
{
    this.textBox1.Text = "Hello World"; //"this." can be omitted
}

Shared event handler

As long as the "contract" of event subscription is met, different event owners can have the same event handler.

For example, in WinForm programming, if it is the same Button type, multiple buttons can share an event handler.

First, we add the "clicked" event handler "buttonclick" to button 1. At this time, we will create a new button. In the event panel of button 2, select "buttonclick" from the drop-down list, so that the event handler of button 2 is also "buttonclick":

private void buttonClicked(object sender, EventArgs e)
{
    textBox1.Text = "Hello!";
}

At this time, clicking button 1 or button 2 can make textBox1 display the text "Hello!"

We can let different buttons do different things. In the implementation code of the event processor, "object sender" represents the event owner, so we can make such modifications to the code:

private void buttonClicked(object sender, EventArgs e)
{
    if (sender == button1)
    {
        textBox1.Text = "This is button 1";
    }
    else if (sender == button2)
    {
        textBox1.Text = "This is button 2";
    }
}

At this time, click button 1 again, and "this is button 1" will be displayed in the text box, while clicking button 2 will display "this is button 2".

Custom event

Complete mode

For example, when a customer enters a restaurant, the waiter comes forward to entertain (subscribing to the customer's "order" event), then the customer orders (an event occurs), and then the waiter handles it accordingly (event processor).

In this case, we need to declare custom events: Order, event owner: Customer class object (Customer), event responder: Waiter class object (waiter).

Before declaring an event, you need to declare a delegate type as a constraint, that is, event subscription, which restricts what event parameters an event can send to the event responder, and when the event processor of the event responder meets the regulations (that is, it meets the signature and return value type specified by the delegate type), the event subscription should save it (that is, the delegate field reference method) . according to the naming convention, the delegate should be named "event name + EventHandler".

After declaring the event member, add the modifier public, the event keyword event and the delegate type that constrains it in front of the event name, and then add and remove its processors in the implementation part.

In addition, we also need a class EventArgs to pass event parameters, but the EventArgs provided by the system cannot pass any data. It is suitable for scenarios that do not need to pass data. If we need to pass data, we need to declare a class derived from EventArgs. According to the naming specification, this class should be named "event name + EventArgs".

using System;
using System.Threading;

namespace TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer(); //Event owner
            Waiter waiter = new Waiter(); //Event responder
            customer.Order += waiter.Action; //Event Order, event handler Action, event subscription+=
            customer.OrderingProcess(); //Trigger the event to simulate the customer ordering process
        }
    }

    //Class used to pass event parameters (event information)
    public class OrderEventArgs : EventArgs
    {
        public String DishName { get; set; } //Indicates the name of the dish
        public String Size { get; set; } //Indicates the specification
    }
    
    //Declare delegate type (event subscription)
    //The first parameter is the event owner, and the second parameter is used to store the relevant information of the order event (event parameters)
    public delegate void OrderEventHandler(Customer customer,OrderEventArgs e);
    
    //Customer: event owner
    public class Customer
    {
        //Create a delegate type field based on the previously declared delegate type to reference the event handler
        private OrderEventHandler orderEventHandler;
        //Declared event:
        //Event is the event keyword, and OrderEventHandler indicates that this delegate is used to constrain the event
        public event OrderEventHandler Order
        {
            //Add event handler
            add
            {
                orderEventHandler += value;
            }
            //Delete event handler
            remove
            {
                orderEventHandler -= value;
            }
        }
        //Simulate customer ordering process:
        public void OrderingProcess()
        {
            Console.WriteLine("Enter enter to start the simulation");
            Console.ReadLine();
            Console.WriteLine("Customers enter the restaurant");
            Thread.Sleep(1000);
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("The customer is ordering...");
                Thread.Sleep(1000);
            }
            //Trigger event:
            if (orderEventHandler != null) //If no event handler exists, the event cannot be triggered
            {
                //Prepare event parameters
                OrderEventArgs e = new OrderEventArgs();
                e.DishName = "Dumplings";
                e.Size = "Large portion";
                //Call event handler
                orderEventHandler(this,e);
            }
        }
    }

    //Waiter class: event responder
    public class Waiter
    {
        public void Action(Customer customer, OrderEventArgs e)
        {
            double price = 0;
            //Calculate the price according to the specification
            switch (e.Size)
            {
                case "Small portion":
                    price = 5;
                    break;
                case "Medium":
                    price = 10;
                    break;
                case "Large portion":
                    price = 15;
                    break;
            }
            Console.WriteLine("Waiter: OK, I'll provide you with one later" 
                + e.Size + e.DishName + ",How much do you need to pay" + price + "Yuan.");
        }
    }
}

//Operation results:
Enter enter to start the simulation

Customers enter the restaurant
 The customer is ordering...
The customer is ordering...
The customer is ordering...
Waiter: OK, I'll provide you with a large order of dumplings later. You need to pay a total of 15 yuan.

Simplified way

The implementation part of the Customer class can be simplified:

You don't need to create fields of delegate type in the class body; When declaring an event, the implementation part of the event can also be omitted; When the event is triggered and the event handler is invoked, the original orderEventHandler field is replaced with an Order event.

Note that C # stipulates that events can only appear on the left side of the + = or - = operator. This simplification is ostensibly against syntax, because it is actually a syntax sugar (that is, it is not as simple as it seems, and some complex underlying principles are automatically implemented by the compiler). If you do not use the simplified method, when calling the event handler, you do not use the delegate field, but directly use Order, and the compiler will report an error: Order can only appear on the left of + = or - =.

public class Customer
{
    //Declare event: omit implementation
    public event OrderEventHandler Order;

    //Simulate customer ordering process:
    public void OrderingProcess()
    {
        Console.WriteLine("Enter enter to start the simulation");
        Console.ReadLine();
        Console.WriteLine("Customers enter the restaurant");
        Thread.Sleep(1000);
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine("The customer is ordering...");
            Thread.Sleep(1000);
        }
        //Trigger event:
        if (Order!= null) //Replace the original orderEventHandler field with Order
        {
            //Prepare event parameters
            OrderEventArgs e = new OrderEventArgs();
            e.DishName = "Dumplings";
            e.Size = "Large portion";
            //Call event handler
            Order(this,e); //Replace the original orderEventHandler field with Order
        }
    }
}

summary

Event essence

From the above, we can see that the essence of an event is an accessor (wrapper) of the delegate field, which restricts the access to the delegate field. It hides most of the functions of the delegate instance from the outside world and only provides an interface to add / remove the event processor. In this way, only the event owner is qualified to trigger the event, making the program more secure.

Why use delegate types to declare events

A delegate type is required to restrict what information (event parameters) the event owner can pass externally.

A delegate type is needed to constrain what signature and return value types the event responder can use to handle the event.

Instances of delegate types are used to store (Reference) event handlers.

Compare events and attributes

Property is the accessor of the field. It only provides some interfaces to access the field to protect the field from abuse; The property itself is not a field.

Events are accessors of delegate fields. They only provide some interfaces for accessing delegate fields to protect delegate fields from abuse; The event itself is not a delegate field.

Tags: C#

Posted on Tue, 12 Oct 2021 23:22:47 -0400 by gtzpower