Delegates are too common and can be used flexibly to make your programming easy.
Simply put, it is an object that can pass the method as a parameter, and also know how to invoke the method.
Simple use of Delegates
A delegate type defines a class of methods that an instance of the type can invoke, which contains the same return type and the same parameters (the same type and number). Delegates, like interfaces, can be defined outside of a class. The following defines a delegate type-Calculator:
delegate int Calculator (int x);
This delegate applies to any method that has an int return type and an int type parameter, such as:
static int Double (int x) {return x * 2;}
Create a delegate instance that assigns the method to the delegate instance:
Calculator C = new Calculator (Double);
Can also be written as:
Calculator C = Double;
This method can be called by a delegate:
int result = C (2);
Here's the full code:
delegate int Calculator (int x); class Program {static int Double (int x) {return x * 2;} static void Main (string[] args) {Calculator c = Double; int result = C (2); Console.Write (result); Console.readkey (); }}
Implement plug-in programming with delegates
We can use a "delegate is an object that can pass a method as a parameter" to implement a plug-in programming.
For example, we have a utility class that implements a common method (Calculate) that performs any method that has an integer parameter and an integer return value. This is a bit abstract, here's an example:
Delegate int calculator (int x); class program { static int double (int x) { return x * 2; } static Void main (String[] args) { int[] values = { 1,2,3,4}; utility.calculate (values, Double); foreach (int i in values) console.write (i + " "); 2 4 6 8 console.readkey (); }}class utility { public static void calculate (int [] values, calculator c) { for (int i = 0;&nbsP;i < values. length; i++) values[i] = c (Values[i]); }}
In this example, the utility is fixed, and the program implements the double function of the integer. We can think of this double method as a plug-in, if in the future we want to implement such as square, cubic calculation, we just need to add plug-ins to the program to continue.
If the double method is temporary and is called only once, and if there is no second call in the entire program, then we can use this plug-in programming more succinctly and flexibly in the Main method, without first defining the method, using the lambda expression, for example:
... Utility.calculate (values, (x) + = {return x * 2;}); ...
We will often write this code in the future.
Multicast delegation
All delegate instances have multicast capabilities. Multicast, just like a bunch of programmers in the temporary network filled out the job intent, one day a company released a job with these programmers exactly matching the work, and then these job seekers were informed-"there is a good job hiring, you can apply to go to work directly!" ”。 PS: For the company, I am also full of spelling. I implore you to allow me to mention the net in blog post. There will be a lot of cases in the follow-up post that I used during the development of the instantaneous network system. :)
That is, a delegate instance can point not only to a method, but also to multiple methods. For example:
MyDelegate d = mymethod1;//"+ =" is used to add, the same as "-=" for removal. D + = mymethod2;//D-= MyMethod2
When called, executes sequentially in the order in which the method is added. Note that for delegates, + = and-= NULL are not error-aware, such as:
MyDelegate d;d + = mymethod1;//equivalent to mydelegate d = MyMethod1;
To better understand the application of multicasting in real-world development, I use the job matching gadget of the mock-up network to do the example. There will be a processing time during the job matching process, so you should be able to see the progress of the execution while performing the match, and also write the progress and execution of the execution to the log file. When a step is processed, two methods are executed to display and record the progress of the execution, respectively.
We define a delegate (Progressreporter) and then define a matching method (match) to execute all the methods in the delegate. As follows:
public delegate void Progressreporter (int. percentcomplete);p Ublic class Utility {public static void Match (Progressrepo Rter p) {if (P! = null) {for (int i = 0; I <=; i++) {p (i * 10); System.Threading.Thread.Sleep (100); } } }}
Then we need two ways to monitor progress, one to write the progress to the console and the other to write the progress to the file. As follows:
Class program { static void main (String[] args) { ProgressReporter p = WriteProgressToConsole; p += WriteProgressToFile; utility.match (P); console.writeline ("done."); console.readkey (); } static void writeprogresstoconsole (Int percentcomplete) { console.writeline (percentcomplete+ "%"); } static void writeprogresstofile (Int percentcomplete) { system.io.file.appendalltext ("Progress.txt", percentcomplete + "%"); }}
Operation Result:
See here, is not found that you are already more in love with C #.
The difference between a static method and an instance method for a delegate
When a method of an instance of a class is assigned to a delegate object, it is not only necessary to maintain the method in context, but also to maintain the instance in which the method resides. The target property of the System.Delegate class points to this instance. As an example:
Class program { static void main (String[] args) { x x = new x (); ProgressReporter p = x.InstanceProgress; p (1); console.writeline (p.Target == x); // true console.writeline (P.Method); // Void instanceprogress (Int32) } static void Writeprogresstoconsole (Int percentcomplete) { Console.WriteLine (percentcomplete+ "%"); } static void Writeprogresstofile (Int percentcomplete) { System.IO.File.AppendAllText ("Progress.txt", percentComplete + "%"); }}class x { Public void instanceprogress (Int percentcomplete) { // do something }}
For static methods, however, the target property of the System.Delegate class is null, so it is better to assign a static method to the delegate.
Generic delegate
If you know generics, it's easy to understand generic delegates, which are simply delegates that contain generic parameters, such as:
Public delegate T calculator<t> (t ARG);
We can change the previous example to a generic example, as follows:
Public delegate t calculator<t> (T arg);class program { static int double (int x) { return x * 2; } static void main (String[] args) { int[] values = { 1, 2, 3, 4 }; utility.calculate (values, double); foreach (int i in values) console.write (i + " "); // 2 4 6 8 console.readkey (); }}class utility { public static void Calculate<T> (t[] values, calculator<t> &NBSP;C) { for (int i = 0; i < values. length; i++) values[i] = c (Values[i]); }}Func and Action Delegate
With generic delegates, there is a generic delegate, Func, and Action that can be applied to any return type and any parameter (type and reasonable number). As shown below (the following in indicates the parameter, out indicates the return result):
Delegate TResult func <out tresult> ();d elegate TResult func <in T, out tresult> (T Arg);d elegate TResult func <in T1, in T2, out tresult> (T1 arg1, T2 arg2); .... Until t16delegate void action ();d elegate void action <in t> (T arg);d elegate void action <in T1, in t2> (T1 arg 1, T2 arg2);.. Until T16.
With this generic delegate, we can delete the calculator generic delegate above, and the example will be more concise:
public static void Calculate<t> (t[] values, func<t,t> c) {for (int i = 0; i < values. Length; i++) Values[i] = C (Values[i]);}
Func and Action delegates, in addition to the ref parameter and out parameters, can be applied to virtually any generic delegate scenario, which is very useful.
The delegate is compatible with 1. The type of the delegate is compatible
delegate void D1 ();d elegate void D2 (); D1 D1 = method1;d2 D2 = d1;
The following are allowed:
D2 D2 = newD2 (D1);
Delegates for the exact same target method are considered equal:
delegate void D (); .... D D1 = method1;d D2 = Method1; Console.WriteLine (D1 = = D2); True
Similarly, for multicast delegates, they are considered equal if they contain the same method and the same order.
2. Parameter type compatible
In OOP, any place that uses a parent class can be substituted with a subclass, and the OOP idea is equally valid for the parameters of the delegate. Such as:
delegate void Stringaction (string s); class program {static void Main () {stringaction sa = new Stringaction (Act Onobject); SA ("Hello"); } static void Actonobject (Object o) {Console.WriteLine (o);//Hello}}
3. Return value type compatible
is compatible with parameter types:
Delegate Object Objectretriever (); class Program {static void Main () {objectretriever o = new Objectretriever (R etrivestring); Object result = O (); Console.WriteLine (result); Hello} static string retrivestring () {return "Hello";}}
Event
When we use the delegate scenario, we would like to have such two roles appearing: broadcasters and Subscribers. We need these two roles to implement a very common scenario of subscribing and broadcasting.
The broadcaster's role should have the ability to include a delegate field to emit a broadcast by invoking a delegate. Subscribers should have the ability to decide when to start or stop a subscription by calling + = and-=.
An event is a word that describes the pattern of the scene. Events are a subset of delegates that are born to meet the needs of the broadcast/subscription mode.
Basic use of events
Declaring an event is simple, just add the event keyword when declaring a delegate object. As follows:
public delegate void Pricechangedhandler (decimal oldprice, decimal newprice);p ublic class iphone6{public event PRICEC Hangedhandler pricechanged;}
The use of events is exactly the same as the delegate, just a few more constraints. Here is an example of a simple event use:
Public delegate void pricechangedhandler (Decimal oldprice, decimal newprice); Public class iphone6 { decimal price; public event pricechangedhandler pricechanged; public decimal price { get { return price; } set { if (Price == value) return; decimal oldPrice = price; price = value; // triggers if the invocation list is not empty. if (pricechanged != null) Pricechanged (oldprice, price); } }} Class program { static void main () { iphone6 iphone6 = new iphone6 () { price = 5288 }; // Subscribe to Events iphone6. pricechanged += iphone6_pricechanged; // Adjust Price (event occurrence) iphone6. Price = 3999; console.readkey (); } static void iphone6_pricechanged (Decimal oldprice, decimal price) { console.writeline ("Year-end promotion,iphone 6 only " + price + " yuan, Original price + oldPrice + Yuan, come to rob! "); }}
Operation Result:
One might ask, if the above event keyword is taken off, the result is not the same, what is the difference?
Yes, you can use the event where you can use a delegate instead.
However, the event has a set of rules and constraints to ensure the security of the program, the event only the + = and-= operation, so that subscribers can only have a subscription or unsubscribe operation, no permission to perform other operations. If it is a delegate, then the Subscriber can use = to reassign the delegate object (all other Subscribers are unsubscribed), even set it to null, even the subscribers can call the delegate directly, these are dangerous operations, the broadcaster loses exclusive control.
Events ensure the security and robustness of the program.
Standard mode for events
The. NET framework defines a standard pattern for event programming. This standard is set up to let. NET Framework is consistent with user code. System.EventArgs is the core of the standard pattern, which is a base class that does not have any members for passing event arguments.
In accordance with the standard pattern, we rewrite the above IPhone6 example. First define the EventArgs:
public class Pricechangedeventargs:eventargs {public readonly decimal oldprice; Public readonly decimal Newprice; Public Pricechangedeventargs (decimal oldprice, decimal newprice) {oldprice = Oldprice; Newprice = Newprice; }}
The delegate is then defined for the event, and the following conditions must be met:
Must be a void return type;
There must be two parameters, and the first is the object type, the second is the EventArgs type (subclass);
Its name must end with EventHandler.
Since it is cumbersome to define your own delegate for each event, the. NET Framework is our pre-defined good one generic delegate system.eventhandler<teventargs>:
public delegate void Eventhandler<teventargs> (object source, Teventargs e) where Teventargs:eventargs;
If the eventhandler<teventargs> of the frame is not used, we need to define one ourselves:
public delegate void Pricechangedeventhandler (object sender, Pricechangedeventargs e);
If no parameters are required, you can use EventHandler directly (<TEventArgs> not required). With Eventhandler<teventargs>, we can define the events in the example like this:
public class IPhone6 {... public event eventhandler<pricechangedeventargs> pricechanged; ...}
Finally, the event standard pattern also needs to write a protected virtual method to trigger the event, which must be prefixed with on, plus the event name (pricechanged), and accept a EventArgs parameter, as follows:
public class IPhone6 {... public event eventhandler<pricechangedeventargs> pricechanged; protected virtual void onpricechanged (Pricechangedeventargs e) {if (pricechanged! = null) pricechanged (this, e); } ...}
The complete example is given below:
Public class pricechangedeventargs : system.eventargs { public readonly decimal OldPrice; public readonly decimal Newprice; public pricechangedeventargs (decimal oldprice, decimal Newprice) { OldPrice = oldPrice; newprice = newprice; }}public class IPhone6 { decimal price; public event Eventhandler<pricechangedeventargs> pricechanged; protected virtual void onpricechanged (pricechangedeventargs e) { if (pricechanged != null) pricechanged (this, e); } public decimal price { get { return price; } set { if (Price == value) return; decimal oldPrice = price; price = value; // triggers if the invocation list is not empty. if (pricechanged != NULL) Onpricechanged (New pricechangedeventargs (oldprice, price)); } }}class program { staTic void main () { iphone6 iphone6 = new iphone6 () { Price = 5288M }; // Subscribe to Event iphone6. pricechanged +=iphone6_pricechanged; // Price adjustment (event occurrence) iphone6. Price = 3999; console.readkey (); } static void iphone6_pricechanged (object sender, pricechangedeventargs e) { console.writeline ("Year-end big sale, iphone 6 only sell " + e.NewPrice + " yuan, price " + e. oldprice + " Yuan, hurry to rob! "); }}
Operation Result:
. NET Delegate