委托的写法的演变

委托的写法的演变yuanl 【转载学习】

.NET 1.x中委托的写法

  委托,如果不追究细节,从表面上来看我们可以将其通俗地理解为一个安全的“函数指针”。当然,这个函数指针其实也是一个对象,有自己的成员,也会封装了被调用方的上下文等等。至于委托的定义和使用方式,则是这样的:

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);
}

  可见,在.NET 1.x中需要使用new DelegateType(...)的方式来创建一个委托对象。不过,作为委托对象内部的方法它既可以是实例方法,也可以是静态方法。此外,方法只需要匹配委托类型的签名和返回值即可,方法参数的名称不会成为约束。

  嗯,就是这么简单。

.NET 2.0中委托的写法

  .NET中的委托引入了范型,且写法略有简化:

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;
}

  在.NET 2.0中,new DelegateType已经可以省略,开发人员可以直接将方法赋值给一个委托对象的引用。当然,这个改进不值一提,.NET 2.0中委托写法的关键在于引入了“匿名方法”:

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);
}

  匿名方法,简单地说就是内联在方法内部的委托对象,它的关键便在于形成了一个闭包(委托执行时所需的上下文)。如上面的代码 中,BeginGetResponse的第一个参数(委托)可以直接使用TestRequest方法的参数url,以及方法内的“局部”变量 request。如果没有匿名函数这个特性的话,代码写起来就麻烦了,例如在.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);
}
}

  此时,我们往往会发现,开发人员需要花费大量的精力,为一小部分代码维护一大段上下文。例如在这段代码中,我们会将url和request对象 塞入一个object数组中,在回调函数中再通过危险的Cast操作恢复数据。如果您希望“强类型”,那么只能为每个回调创建一个新的上下文对象,维护起 来可能更加麻烦——要知道,在并行编程,异步调用越来越重要的今天,如果没有匿名方法自动保留上下文的特性,开发人员会为这些“额外工作”疲于奔命的。

  可能您会说,匿名方法的可读性不佳,因为需要“内联”。一个方法中内联太多,维护成本就上去了,所以匿名方法并不推荐使用。我想说的是,您错了。如果为了可维护性,要将方法独立拆开,也可以利用匿名方法的优势:

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);
}
}

  如果借助.NET 3.5中的Lambda表达式,代码可以写的更简单易读:

public static void TestRequest(string url)
{
WebRequest request = HttpWebRequest.Create(url);
request.BeginGetResponse(ar => TestAsyncCallback(ar, request, url), null);
}

匿名方法的作用

  千万不要小看匿名方法的作用,有些时候您认为它的作用仅限于上文描述,只是因为没有在某些问题上踏前一步。例如,对于那些只需要“按需创建”,且要“线程安全”的对象,您会怎么做呢?没错,可以使用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;
}
}

  嗯,做的很漂亮!那么……这样的属性再来一个,再来三个,再来五个呢?可能有些朋友就会开始大段地Copy & Paste,于是错误便难免了。这里有一件真人真事,以前某位同学在一堆这样的代码中迷茫了,说为什么用了这种方法,还是初始化了多次对象了?检查了半天 没有看出问题来。最后发现,原因是访问了错误的initialized变量(例如,在某个应该访问artistInitialized的地方访问了 articleInitialized)。可惜,大段时间已经被浪费了——更糟的是,心情也随之变差了。

  其实,Copy & Paste很明显没有遵守DRY原则啊。为什么不把它们封装在一处呢?例如:

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;
}
}
}

  于是,之前的代码就可以简化成这样了:

private Lazy<BigInstance> m_lazyInstance =
new Lazy<BigInstance>(delegate { return new BigInstance(); });

public BigInstance Instance { get { return this.m_lazyInstance.Value; } }

  还是太丑,上Lambda表达式!

private Lazy<BigInstance> m_lazyInstance =
new Lazy<BigInstance>(() => new BigInstance());
public BigInstance Instance { get { return this.m_lazyInstance.Value; } }

  如果没有匿名方法,许多容易使用的编程模型和方式都难以开展。例如,我们就不会有CacheHelper,也不会有AsyncTaskDispatcher(上),也很难利用“延迟”所带来的便利,更难以出现微软并行扩展、CCR等优秀框架。可以这么说,如果您不善于使用委托,您如果不知道如何合适地使用匿名方法,您在不自知的情况下可能就已经编写了大量额外的代码了。

你可能感兴趣的:(写)