技术开发 频道

.NET中*延迟*特性的几个陷阱

使用using来管理文件的打开关闭是最容易不过的事情了,不过现在如果您通过ReadAllText(@"C:\abc.txt")方法获得的Func<string>对象,在执行时就会抛出ObjectDisposedException。这是因为原本我们意图中的顺序:

 打开文件

 读取内容

 关闭文件

 因为有“延迟”特性,这个顺序已经变为:

 打开文件

 关闭文件

 读取内容

 这怎么能不出错?

 解决方案

 有朋友说,这个容易:

static Func<string> ReadAllText(string file)

 {

 using (Stream stream = File.OpenRead(file))

 {

 StreamReader reader = new StreamReader(stream);

 string text = reader.ReadToEnd();

 return () => text;

 }

 }

 的确没有抛出异常了,但是这也丧失了“延迟”的特点了。我们必须让它能够在调用委托对象的时候,才去打开文件:

static Func<string> ReadAllText(string file)

 {

 return () =>

 {

 using (Stream stream = File.OpenRead(file))

 {

 StreamReader reader = new StreamReader(stream);

 return reader.ReadToEnd();

 }

 };

 }

 值得一提的是,using完全可以配合yield语句使用。也就是说,您可以编写这样的代码

static IEnumerable<string> AllLines(string file)

 {

 using (Stream stream = File.OpenRead(file))

 {

 StreamReader reader = new StreamReader(stream);

 while (!reader.EndOfStream)

 {

 yield return reader.ReadLine();

 }

 }

 }

 由此也可见C#编译器是多么的强大,它帮我们解决了非常重要的问题。

 闭包共享

 问题

 其实这个问题也已经被谈过很多次了,在这里提一下主要是为了保持内容的完整性。您认为,以下代码结果如何?

 List<Action> actions = new List<Action>();

 for (int i = 0; i < 10; i++)

 {

 actions.Add(() => Console.WriteLine(i));

 }

 foreach (var a in actions) a();

 它打印出来的结果是10个10,具体原因在《警惕匿名方法造成的变量共享》一文中已经有过描述,概括而来便是:各个action共享一个闭包,导致其中的“i”并不是独立的。

 解决方案

 解决这个问题的方法,只需让不同闭包访问的值相互独立即可。如:

 List<Action> actions = new List<Action>();

 for (int i = 0; i < 10; i++)

 {

 int  j = i; // 新增代码

 actions.Add(() => Console.WriteLine(j));

 }

 foreach (var a in actions) a();

原文地址:http://www.cnblogs.com/jeffreyzhao/archive/2009/06/08/laziness-traps.html

0
相关文章