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

2009-06-08 13:19  Jeffrey Zhao  阅读(...)  评论(...编辑  收藏

.NET发展至今，其实各处都有“延迟（Lazy）”的痕迹，一个小小的“Laziness”给我们带来了不少灵活性。“延迟”的关键就在于“只在需要的时候处理数据”，老赵曾经在多篇文章中提到了类似的概念，如《高阶函数、委托与匿名方法》及《您善于使用匿名函数吗？》。不过“延迟”本身也会给您带来一些陷阱，某些陷阱您很有可能也曾经遇到过。这篇文章便是总结了延迟特性的集中常见陷阱，并给出应对方案。

# 重复运算

## 问题

“延迟”的本意是“减少计算”，但是如果您使用不当，很可能反而会造成“重复计算”。例如，我们首先构建一个方法，它接受一个参数n，返回一个Func<int, bool>对象：

static Func<int, bool> DivideBy(int n)
{
return x =>
{
bool divisible = x % n == 0;
Console.WriteLine(
"{0} can be divisible by {1}? {2}",
x, n, divisible ? "Yes" : "No");
return divisible;
};
}


List<int> values = new List<int>();
for (int i = 0; i < 10; i++) values.Add(i);

var divideByTwo = values.Where(DivideBy(2));
var divideByTwoAndThree = divideByTwo.Where(DivideBy(3));
var divideByTwoAndFive = divideByTwo.Where(DivideBy(5));

foreach (var i in divideByTwoAndThree) { }
foreach (var i in divideByTwoAndFive) { }


0 can be divisible by 2? Yes
0 can be divisible by 3? Yes
1 can be divisible by 2? No
2 can be divisible by 2? Yes
2 can be divisible by 3? No
3 can be divisible by 2? No
4 can be divisible by 2? Yes
4 can be divisible by 3? No
5 can be divisible by 2? No
6 can be divisible by 2? Yes
6 can be divisible by 3? Yes
7 can be divisible by 2? No
8 can be divisible by 2? Yes
8 can be divisible by 3? No
9 can be divisible by 2? No
0 can be divisible by 2? Yes
0 can be divisible by 5? Yes
1 can be divisible by 2? No
2 can be divisible by 2? Yes
2 can be divisible by 5? No
3 can be divisible by 2? No
4 can be divisible by 2? Yes
4 can be divisible by 5? No
5 can be divisible by 2? No
6 can be divisible by 2? Yes
6 can be divisible by 5? No
7 can be divisible by 2? No
8 can be divisible by 2? Yes
8 can be divisible by 5? No
9 can be divisible by 2? No

## 解决方案

var divideByTwo = values.Where(DivideBy(2)).ToList();
var divideByTwoAndThree = divideByTwo.Where(DivideBy(3));
var divideByTwoAndFive = divideByTwo.Where(DivideBy(5));


0 can be divisible by 2? Yes
1 can be divisible by 2? No
2 can be divisible by 2? Yes
3 can be divisible by 2? No
4 can be divisible by 2? Yes
5 can be divisible by 2? No
6 can be divisible by 2? Yes
7 can be divisible by 2? No
8 can be divisible by 2? Yes
9 can be divisible by 2? No
0 can be divisible by 3? Yes
2 can be divisible by 3? No
4 can be divisible by 3? No
6 can be divisible by 3? Yes
8 can be divisible by 3? No
0 can be divisible by 5? Yes
2 can be divisible by 5? No
4 can be divisible by 5? No
6 can be divisible by 5? No
8 can be divisible by 5? No


# 异常陷阱

## 问题

public static IEnumerable<string> ToString(IEnumerable<int> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}

foreach (int item in source)
{
yield return item.ToString();
}
}


static void Main(string[] args)
{
IEnumerable<string> values;
try
{
values = ToString(null);
}
catch (ArgumentNullException)
{
Console.WriteLine("Passed the null source");
return;
}

foreach (var s in values) { }
}


## 解决方案

public static IEnumerable<string> ToString(IEnumerable<int> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}

}

private static IEnumerable<string> ToStringInternal(IEnumerable<int> source)
{
foreach (int item in source)
{
yield return item.ToString();
}
}


# 资源管理

## 问题

static Func<string> ReadAllText(string file)
{
using (Stream stream = File.OpenRead(file))
{
}
}


1. 打开文件
2. 读取内容
3. 关闭文件

1. 打开文件
2. 关闭文件
3. 读取内容

## 解决方案

static Func<string> ReadAllText(string file)
{
using (Stream stream = File.OpenRead(file))
{

return () => text;
}
}


static Func<string> ReadAllText(string file)
{
return () =>
{
using (Stream stream = File.OpenRead(file))
{
}
};
}


static IEnumerable<string> AllLines(string file)
{
using (Stream stream = File.OpenRead(file))
{
{
}
}
} 

# 闭包共享

## 问题

List<Action> actions = new List<Action>();
for (int i = 0; i < 10; i++)
{
}

foreach (var a in actions) a();


## 解决方案

List<Action> actions = new List<Action>();
for (int i = 0; i < 10; i++)
{
int  j = i; // 新增代码