C #/Delegate and event in. net

Source: Internet
Author: User
C #/Delegate and event in. net

--------------------------------------------------------------------------------

Directory
Translator's words

Overview

Delegate)

Call the method directly-without Delegation
The most basic delegate
Call static methods
Call the member Method
Multi-channel broadcast

Events)

Convention
Example of a simple event
Second event example

Conclusion

--------------------------------------------------------------------------------

By luben)
Delegation and events are hard to understand for beginners. I have seen about the entrusting party beforeArticle. Net delegates: a c # bedtime story. It is well written and has a Chinese version online. But for beginners, the following article seems to be more easy to understand, so it is a special translation for beginners to learn. Original English address: delegates and events in C #/. net

Overview
Today, all kinds of event-driven programming methods are filled with our vision. C # inject new vigor into the event-driven programming world by supporting events and delegates. This article focuses on how to add an event to a common UI control.Program(Event handle. By adding an addonclick or similar event to the button class through a simple simulation, we will explore the true story behind the scenes. This will help us better understand the nature of Event Handlers using multi cast delegates :)

Delegates (delegate)
The delegate in C # is similar to the function pointer in C or C ++. With delegation, programmers can encapsulate a reference to a method in the delegate object. Then the delegate object will be passed toCodeInstead of knowing which method is called at the time of compilation. (Translator's note: if you do not understand this section, let's take a look at it and try again)

Call the method directly-without Delegation
In most cases, when we call a method, we directly specify the method to be called. For example, if the class myclass has a method named process, we usually call it (simplesample. CS) like this ):

Using system;

Namespace akadia. nodelegate
{
Public class myclass
{
Public void process ()
{
Console. writeline ("process () begin ");
Console. writeline ("process () End ");
}
}

Public class test
{
Static void main (string [] ARGs)
{
Myclass = new myclass ();
Myclass. Process ();
}
}
}

In most cases, this is enough. However, sometimes we don't want to call a method directly-we want to pass it to another object so that it can call it. This is very useful in the event-driven system, such as the graphical user interface. When we want to execute some code when the user clicks the button, or when we want to record the log, but I don't know how to record it.

The most basic delegate
A very interesting and useful feature of delegation is that it does not know or care about the category of Methods referenced by it. Any method is acceptable, as long as the parameter type and return value type of this method can match it. This feature makes the delegate very suitable for anonymous calls ("anonymous" Invocation ).

The single-channel broadcast delegated structure (Signature) is as follows:

Delegate result-type identifier ([parameters]);

Here:

Result-type: Type of the returned value, which is the same as the type of the returned value of the method.
Identifier: name of the Delegate
Parameters: parameter of the method to be referenced
Example:

Public Delegate void simpledelegate ()

This Declaration defines a delegate named simpledelegate, which can encapsulate any method without parameters and return values.

Public Delegate int buttonclickhandler (Object obj1, object obj2)

This Declaration defines a delegate named buttonclickhandler, which can encapsulate any method with two objec parameters to return int type values.

The delegate allows us to specify what the called method looks like, rather than the specific method to be called. The delegate declaration looks like a method declaration. Except for one case, the method we want to declare is exactly the method that can be referenced by the delegate.

There are three steps to define and use delegation:

Statement
Instantiation
Call
A basic example (simpledelegate1.cs ):

Using system;

Namespace akadia. basicdelegate
{
// Statement
Public Delegate void simpledelegate ();

Class testdelegate
{
Public static void myfunc ()
{
Console. writeline ("I was called by Delegate ...");
}

Public static void main ()
{
// Instantiate
Simpledelegate = new simpledelegate (myfunc );

// Call
Simpledelegate ();
}
}
}

Compilation test:

# CSC simpledelegate1.cs
# Simpledelegate1.exe
I was called by Delegate...

Call static methods
The following is a complex example (simpledelegate2.cs) that declares a delegate with a string type parameter that does not return values:

Using system;

Namespace akadia. simpledelegate
{
// Delegate Definition
Public class myclass
{
// Declare a delegate with the string parameter and no return value
Public Delegate void loghandler (string message );

// Use the delegate, just like using it. However, we need to check whether the delegate is empty before calling (the delegate does not reference any method)
Public void process (loghandler)
{
If (loghandler! = NULL)
{
Loghandler ("process () begin ");
}

If (loghandler! = NULL)
{
Loghandler ("process () End ");
}
}
}

// Test the application and use the delegate defined above
Public class testapplication
{
// Static method: It will be referenced by the delegate. To call the process () method,
// We define a log logging method: logger () that matches the delegate Structure ()
Static void logger (string S)
{
Console. writeline (s );
}

Static void main (string [] ARGs)
{
Myclass = new myclass ();

// Create a delegate instance and reference the LOG method defined above. The delegate will be passed to the process () method.
Myclass. loghandler mylogger = new myclass. loghandler (logger );
Myclass. Process (mylogger );
}
}
}

Compilation test:

# CSC simpledelegate2.cs
# Simpledelegate2.exe
Process () begin
Process () End

Call the member Method
In the preceding simple example, the logger () method only writes strings. We may need a different method to write the log information to the file. To achieve this, we need to know which file the log information is written to (simpledelegate3.cs ):

Using system;
Using system. IO;

Namespace akadia. simpledelegate
{
// Definition of delegation
Public class myclass
{
// Declare a delegate with a string parameter that does not return a value
Public Delegate void loghandler (string message );

// Use the delegate, just like using it. However, we need to check whether the delegate is empty before calling (the delegate does not reference any method)
Public void process (loghandler)
{
If (loghandler! = NULL)
{
Loghandler ("process () begin ");
}

If (loghandler! = NULL)
{
Loghandler ("process () End ");
}
}
}

// Class that encapsulates file I/O operations
Public class filelogger
{
Filestream;
Streamwriter;

// Constructor
Public filelogger (string filename)
{
Filestream = new filestream (filename, filemode. Create );
Streamwriter = new streamwriter (filestream );
}

// The member method to be used in the delegate
Public void logger (string S)
{
Streamwriter. writeline (s );
}

Public void close ()
{
Streamwriter. Close ();
Filestream. Close ();
}
}

// Delegate the logger () method pointing to the filelogger class instance F1.
// When the delegate is called in process (), the member method logger () will also be called, and the log will be written to the specified file.
Public class testapplication
{
Static void main (string [] ARGs)
{
Filelogger FL = new filelogger ("process. log ");

Myclass = new myclass ();

// Create a delegate instance and reference the LOG method defined above. The delegate will be passed to the process () method.
Myclass. loghandler mylogger = new myclass. loghandler (FL. Logger );
Myclass. Process (mylogger );
Fl. Close ();
}
}
}

This is cool because we don't need to change the process () method. no matter whether the delegate references the static or member method, the delegate definition calls part of the code.

Test Compilation:

# CSC simpledelegate3.cs
# Simpledelegate3.exe
# Cat process. Log
Process () begin
Process () End

Multicasting)
It is good to reference the member method, but we can do more with delegation. In C #, the delegate is a multi-channel broadcast (Multicast), that is, they can point to multiple methods at a time. A multi-channel broadcast delegate maintains a list of methods that will be called when the delegate is called.

Using system;
Using system. IO;

Namespace akadia. simpledelegate
{
// Definition of delegation
Public class myclass
{
// Declare a delegate with a string parameter that does not return a value
Public Delegate void loghandler (string message );

// Use the delegate, just like using it. However, we need to check whether the delegate is empty before calling (the delegate does not reference any method ).
Public void process (loghandler)
{
If (loghandler! = NULL)
{
Loghandler ("process () begin ");
}

If (loghandler! = NULL)
{
Loghandler ("process () End ");
}
}
}

// Class that encapsulates file I/O operations
Public class filelogger
{
Filestream;
Streamwriter;

// Constructor
Public filelogger (string filename)
{
Filestream = new filestream (filename, filemode. Create );
Streamwriter = new streamwriter (filestream );
}

// The member method to be used in the delegate
Public void logger (string S)
{
Streamwriter. writeline (s );
}

Public void close ()
{
Streamwriter. Close ();
Filestream. Close ();
}
}

// Call multiple delegated test applications
Public class testapplication
{
// The static method to be used in the delegate
Static void logger (string S)
{
Console. writeline (s );
}

Static void main (string [] ARGs)
{
Filelogger FL = new filelogger ("process. log ");

Myclass = new myclass ();

// Create a delegate instance and reference testapplication
// Static method defined in logger () and filelogger class instance F1 member method.
Myclass. loghandler mylogger = NULL;
Mylogger + = new myclass. loghandler (logger );
Mylogger + = new myclass. loghandler (FL. Logger );

Myclass. Process (mylogger );
Fl. Close ();
}
}
}

Test Compilation:

# CSC simpledelegate4.cs
# Simpledelegate4.exe
Process () begin
Process () End
# Cat process. Log
Process () begin
Process () End

Events)
The event model in C # is based on the event programming model, which is very common in asynchronous programming. This programming model comes from the idea of "publisher and subscribers. In this model, the publisher (subscribers) processes some logic to publish an "Event", and they only publish these events to the subscriber (publishers) who subscribed to the event ).

In C #, any object can publish a group of events that other applications can subscribe. When these release classes (the classes that publish these events) trigger this event, all applications that subscribe to this event will be notified. The following figure shows the mechanism:

Convention
The following are some important practices for using events:

Event Handlers in. NET Framework does not return any value, with two parameters
The first parameter is the event source, that is, the object that publishes the event (Note: specifically, it should be an instance of the class that publishes the event)
The second parameter is an object that inherits eventargs.
Events exist as attributes of the published classes.
The keyword event controls how the event subscription class accesses this event attribute.
Note: eventhandler is the event Delegate type that comes with. NET Framework. As described above, it has two parameters. If the published event class does not have data to be transmitted to the subscription class, it is sufficient to use the eventhandler delegate that comes with the system. If the clock data needs to be passed to the subscription class like the second example of an event, a custom delegate is usually used to include a parameter class that inherits eventargs, let this parameter class encapsulate the data to be transferred (for example, Example 2 ).

Example of a simple event
Next we don't need to delegate, but use events to modify and implement the above log example

Using system;
Using system. IO;

Namespace akadia. simpleevent
{
/* =========== Publisher of the event ====================== */
Public class myclass
{
// Define a delegate named loghandler, which encapsulates methods with a string parameter that does not return any value
Public Delegate void loghandler (string message );

// Define events based on the delegate defined above
Public event loghandler log;

// Replace the process () method that uses the delegate as the parameter.
// Call the event. The onxxxx method is used. XXXX is the name of the event.

Public void process ()
{
Onlog ("process () begin ");
Onlog ("process () End ");
}

// Prevent the event from being empty and create an onxxxx method call event
Protected void onlog (string message)
{
If (log! = NULL)
{
Log (Message );
}
}
}

// Class that encapsulates file I/O operations
Public class filelogger
{
Filestream;
Streamwriter;

// Constructor
Public filelogger (string filename)
{
Filestream = new filestream (filename, filemode. Create );
Streamwriter = new streamwriter (filestream );
}

// The member method to be used in the delegate
Public void logger (string S)
{
Streamwriter. writeline (s );
}

Public void close ()
{
Streamwriter. Close ();
Filestream. Close ();
}
}

/* ========= Subscriber of the event ========================== */
// Now it is easier and clearer to add a delegated instance to the event
Public class testapplication
{
Static void logger (string S)
{
Console. writeline (s );
}

Static void main (string [] ARGs)
{
Filelogger FL = new filelogger ("process. log ");
Myclass = new myclass ();

// Subscribe to the logger method and f1.logger
Myclass. log + = new myclass. loghandler (logger );
Myclass. log + = new myclass. loghandler (FL. Logger );

// Trigger the process () method
Myclass. Process ();

Fl. Close ();
}
}
}

Compilation test:

# CSC simpleevent. CS
# Simpleevent.exe
Process () begin
Process () End
# Cat process. Log
Process () begin
Process () End

Second event example
Suppose we want to create a clock class. When the local time changes every second, this class uses events to notify potential subscribers. See the example:

Using system;
Using system. Threading;

Namespace secondchangeevent
{
/* = ============================= */

// For the clock class observed by other classes, change the class to publish an event: secondchange. Observe that the class of this class subscribes to this event.
Public class clock
{
// Private variables for hours, minutes, And seconds
Private int _ hour;
Private int _ minute;
Private int _ second;

// Define the delegate named secondchangehandler to encapsulate methods that do not return values,
// This method includes parameters, a clock type object parameter, and a timeinfoeventargs type object
Public Delegate void secondchangehandler (
Object clock,
Timeinfoeventargs timeinformation
);

// Events to be released
Public event secondchangehandler secondchange;

// Method for triggering an event
Protected void onsecondchange (
Object clock,
Timeinfoeventargs timeinformation
)
{
// Check if there are any subscribers
If (secondchange! = NULL)
{
// Call the event
Secondchange (clock, timeinformation );
}
}

// Let the clock run and trigger an event every second
Public void run ()
{
For (;;)
{
// Let the thread sleep for one second
Thread. Sleep (1000 );

// Obtain the current time
System. datetime dt = system. datetime. now;

// Notify the subscriber if the second changes
If (Dt. Second! = _ Second)
{
// Create a timeinfoeventargs object and send it to the subscriber.
Timeinfoeventargs timeinformation =
New timeinfoeventargs (
DT. Hour, DT. Minute, DT. Second );

// Notify the subscriber
Onsecondchange (this, timeinformation );
}

// Update status information
_ Second = DT. Second;
_ Minute = DT. minute;
_ Hour = DT. hour;

}
}
}

// This class is used to store effective information about events,
// It is also used to store additional clock status information that needs to be sent to the subscriber.
Public class timeinfoeventargs: eventargs
{
Public timeinfoeventargs (INT hour, int minute, int second)
{
This. Hour = hour;
This. Minute = minute;
This. Second = second;
}
Public readonly int hour;
Public readonly int minute;
Public readonly int second;
}

/* ================================== Event subscribers ================== ============================= */

// A subscriber. Displayclock subscribes to clock events. Its job is to display the current event.
Public class displayclock
{
// Input a clock object and subscribe to its secondchangehandler event
Public void subscribe (clock theclock)
{
Theclock. secondchange + =
New clock. secondchangehandler (timehaschanged );
}

// Implement the delegate matching type Method
Public void timehaschanged (
Object theclock, timeinfoeventargs Ti)
{
Console. writeline ("Current Time: {0 }:{ 1 }:{ 2 }",
Ti. Hour. tostring (),
Ti. Minute. tostring (),
Ti. Second. tostring ());
}
}

// The second subscriber writes the current time to a file
Public class logclock
{
Public void subscribe (clock theclock)
{
Theclock. secondchange + =
New clock. secondchangehandler (writelogentry );
}

// This method should have written information into a file
// Here we use the information output console instead
Public void writelogentry (
Object theclock, timeinfoeventargs Ti)
{
Console. writeline ("logging to file: {0 }:{ 1 }:{ 2 }",
Ti. Hour. tostring (),
Ti. Minute. tostring (),
Ti. Second. tostring ());
}
}

/* = ============================= */

// Test the owned Program
Public class test
{
Public static void main ()
{
// Create a clock instance
Clock theclock = new clock ();

// Create a displayclock instance to subscribe to the previously created clock event
Displayclock Dc = new displayclock ();
DC. subscribe (theclock );

// Create a logclock instance to subscribe to the previously created clock event
Logclock lc = new logclock ();
LC. subscribe (theclock );

// Run the clock
Theclock. Run ();
}
}
}

Conclusion
In the last example, the clock class can simply implement the printing time rather than trigger the event. So why is it annoying to introduce the use of delegation? The biggest benefit of using the publish/subscribe mode is to notify any number of subscription classes when an event is triggered. These subscription classes do not need to know how the clock class works, and this clock class does not need to know how subscribers respond to this event. Similarly, a button can publish an onclick event. Any number of objects that do not want to close the event can subscribe to this event and will be notified when this button is clicked.

The publisher and subscriber can be well decoupled through delegation. This greatly enhances scalability and robustness. The clock class can change its way of viewing time without interfering with those subscription classes. The subscription class can also change its response to time change events without disturbing the clock class. The two classes are independent from each other, making maintenance code easier.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.