By Lao Zhao (Zhao Yu, Network nameLao Zhao, Foreign nameJeffrey ZhaoHe is currently a researcher at the product development department of Shanda Innovation Institute .)
In his article "feelings about recent interviews", Michael talked about his question about "How to Write delegate in. Net framework1.1, 2.0, and 3.5" during the interview. As a result, a friend replied, "I would like to ask the landlord, but there are several ways to write "and "Contemporary Kong Yiji. After reading all the comments, except some friends who think that "I really shouldn't know this problem", it seems that no one is explicitly supporting the landlord.
But I support it. Why? Because I have raised such a problem.
In this way, we will not apply for "senior developers" for the moment. Should we know this answer on the premise of "claiming to be familiar with various versions of. Net frameworks. For the moment, we do not mention the "purpose" of Michael's question. Lao Zhao explained the problem separately and then talked about why he raised the question.
There may be one thing that needs to be mentioned above: the Commission itself has never changed, and the change has always been the "writing" of the Commission ". So more specifically, the change is just the "compiler ". All the content in this article is implemented using C #. In fact, the C # compiler itself is also discussed-But VB. NET has also changed. Since the. NET version is closely related to the C # version, the full text uses the. NET version.
Delegate writing in. Net 1.x
Delegate, if you do not investigate the details, on the surface, we can consider it as a safe "function pointer ". Of course, this function pointer is actually an object that has its own members and encapsulates the context of the called party. The definition and usage of delegation are as follows:
public delegate int SomeDelegate(string arg1, bool arg2);public static int SomeMethod(string arg1, bool arg2) { return 0; }public class SomeClass{ public int SomeMethod(string a1, bool a2) { return 0; } public event SomeDelegate SomeEvent;}static void Main(string[] args){ SomeClass someClass = new SomeClass(); SomeDelegate someDelegate = new SomeDelegate(someClass.SomeMethod); someClass.SomeEvent += new SomeDelegate(SomeMethod);}
It can be seen that in. Net 1. X, you must use the new delegatetype (...) method to create a delegate object. However, the internal method of the delegate object can be either an instance method or a static method. In addition, the method only needs to match the signature and return value of the delegate type. The name of the method parameter is not restricted.
Well, it's that simple.
Simplified anonymous method and callback
The delegation in. NET 2.0 introduces the paradigm, and the writing is slightly simplified:
public delegate TResult MyFunc<T1, T2, TResult>(T1 a1, T2 a2);public static int SomeMethod(string a1, bool a2) { return 0; }static void Main(string[] args){ MyFunc<string, bool, int> myFunc = SomeMethod;}
In. NET 2.0, new delegatetype can be omitted. developers can directly assign methods to references of a delegate object. Of course, this improvement is not worth mentioning. The key to delegate writing in C #2.0 is to introduce the "anonymous method ":
public static void TestRequest(string url){ WebRequest request = HttpWebRequest.Create(url); request.BeginGetResponse(delegate(IAsyncResult ar) { using (WebResponse response = request.EndGetResponse(ar)) { Console.WriteLine("{0}: {1}", url, response.ContentLength); } }, null);}
In short, an anonymous method is a delegate object embedded in the method. The key to this method is to form a closure (the context required for delegate execution ). In the code above, the first parameter (delegate) of begingetresponse can directly use the parameter URL of the testrequest method and the "local" variable request in the method. Without the anonymous function feature, you may have to write the code like this in. Net 1.x:
Public static void testrequest (string URL)
{
Webrequest request = httpwebrequest. Create (URL );
Object [] context = new object [] {URL, request };
Request. begingetresponse (testasynccallback, context );
}
Public static void testasynccallback (iasyncresult AR)
{
Object [] context = (object []) Ar. asyncstate;
String url = (string) Context [0];
Webrequest request = (webrequest) Context [1];
Using (webresponse response = request. endgetresponse (AR ))
{
Console. writeline ("{0 }:{ 1}", URL, response. contentlength );
}
}
At this time, we often find that developers need to spend a lot of effort to maintain a large context for a small part of the code. For example, in this code, we will insert the URL and request object into an object array and restore data through the dangerous cast operation in the callback function. If you want a "strong type", you can only create a new context object for each callback, which may be more difficult to maintain-you know, in parallel programming, asynchronous calls are becoming more and more important today, if there is no anonymous method to automatically retain the context features, developers will be exhausted for these "extra work.
You may say that the anonymous method is not readable because it requires "inline ". There are too many inline entries in a method, and the maintenance cost is increased. Therefore, it is not recommended to use anonymous methods. You are wrong. If you want to separate the methods for maintainability, you can also take advantage of the anonymous method:
public static void TestRequest(string url){ WebRequest request = HttpWebRequest.Create(url); request.BeginGetResponse(delegate(IAsyncResult ar) { TestAsyncCallback(ar, request, url); }, null);}public static void TestAsyncCallback(IAsyncResult ar, WebRequest request, string url){ using (WebResponse response = request.EndGetResponse(ar)) { Console.WriteLine("{0}: {1}", url, response.ContentLength); }}
With lambda expressions in. Net 3.5, the code can be easier to read:
public static void TestRequest(string url){ WebRequest request = HttpWebRequest.Create(url); request.BeginGetResponse(ar => TestAsyncCallback(ar, request, url), null);}
Case Study of the anonymous method: delayed initializer
Do not underestimate the role of the anonymous method. In some cases, you think that its role is limited to the above description, just because it has not taken a step forward on some issues. For example, what do you do for objects that only need to be created on demand and require "thread security? Yes, you can use double check:
private object m_mutex = new object();private bool m_initialized = false;private BigInstance m_instance = null;public BigInstance Instance{ get { if (!this.m_initialized) { lock (this.m_mutex) { if (!this.m_initialized) { this.m_instance = new BigInstance(); this.m_initialized = true; } } } return this.m_instance; }}
Well, it's pretty! So ...... Here is one more property, three more, and five more? Some friends may start to copy & Paste in a large segment, so errors are inevitable. There is a real thing here. Someone was confused in a pile of such code before. Why did I use this method or initialize the object multiple times? After checking for half a day, I did not see the problem. The final cause is that the incorrect initialized variable is accessed (for example, articleinitialized is accessed in a place where artistinitialized should be accessed ). Unfortunately, it has been a waste of time-Worse, the mood is also deteriorated.
In fact, copy & paste obviously does not follow the dry principle. Why not encapsulate them in one place? For example:
Public class lazy <t>
{
Public lazy (func <t> func)
{
This. m_initialized = false;
This. m_func = func;
This. m_mutex = new object ();
}
Private func <t> m_func;
Private bool m_initialized;
Private object m_mutex;
Private t m_value;
Public T value
{
Get
{
If (! This. m_initialized)
{
Lock (this. m_mutex)
{
If (! This. m_initialized)
{
This. m_value = This. m_func ();
This. m_func = NULL;
This. m_initialized = true;
}
}
}
Return this. m_value;
}
}
}
Therefore, the previous code can be simplified as follows:
private Lazy<BigInstance> m_lazyInstance = new Lazy<BigInstance>(delegate { return new BigInstance(); });public BigInstance Instance { get { return this.m_lazyInstance.Value; } }
It's too ugly. Use the lambda expression!
private Lazy<BigInstance> m_lazyInstance = new Lazy<BigInstance>(() => new BigInstance());public BigInstance Instance { get { return this.m_lazyInstance.Value; } }
Without anonymous methods, many easy-to-use programming models and methods are difficult to implement. For example, we don't have cachehelper, or asynctaskdispatcher (Top, bottom), or the convenience brought by "latency, it is even more difficult to see excellent frameworks such as Microsoft parallel expansion and Cr. In this case, if you are not good at using delegation and you do not know how to properly use the anonymous method, you may have compiled a large amount of additional code without knowing it.
One of Zhao's daily work is to provide a variety of extended APIs for the project, so that programmers can enjoy the development work more happily, get better productivity, and make the Code better. Now C # has excellent language features such as anonymous method, Lambda expression, Expression Tree, and extension method, which makes me feel like a duck. Therefore, I can say that Java is a tough language (Java friends can learn Scala quickly ). When reading a lot of Java open-source project code, I often feel like this: "If it is C #, you can skip this class by using the anonymous method, that class can be omitted ......". Yes, creating some classes to keep the context of the callback function is indeed an incredible thing for C # programmers.
As for lambda expressions and other topics, let's talk about them next time.
Disadvantages of anonymous methods
The advantage of the anonymous method is that it automatically forms a closure, and its disadvantage is that it allows programmers to create a closure "unconsciously", which will prolong the lifecycle of some objects. For example, in the testrequest method at the beginning, the URL seems to be a parameter, and the request is a local variable. Some friends may think that they are ready to be recycled after the method exits. However, because of the closure, the URL and request have been "upgraded" as the domain variable of an object. its lifecycle is extended until the callback function is executed. Therefore, some inexplicable situations may occur if you do not pay attention to them.
In fact, these are traps brought about by "latency". As a good developer, apart from knowing the role and advantages of something, you also need to know its problems, right?
Summary
Do you still think that "delegation" in. NET is simple and only suitable for "beginners?
You may say "yes", but it is really not easy for me, and I will gradually realize this. Although a large amount of delegated content can be searched on the Internet, there are still many things that need to be accumulated in a large number of programming practices-Wait a moment, isn't that one of the main differences between "senior developers" and "beginners?
As a friend of Kong Yiji, you may lose a chance to understand Chinese traditional culture while ignoring the four types of "NLP!
Let's talk about the rest next time. Lambda expressions are still waiting for us.