技术开发 频道

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

 【IT168技术文档】

转载自Jeff Zhao的博客

    重复运算

 问题

 “延迟”的本意是“减少计算”,但是如果您使用不当,很可能反而会造成“重复计算”。例如,我们首先构建一个方法,它接受一个参数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;

 };

 }

 返回的Func<int, bool>对象会根据传入的参数x,返回一个表示x能否被n整除的布尔值。在这过程中,还会向控制台输出一句话,例如:“10 can be divisible by 3? No”。每当看到这句话,则表明“经过了一次判断”。那么您是否知道,下面的代码会输出什么结果呢?

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

 您是否发现,无论是在遍历divideByTwoAndThree和divideByTwoAndFive序列时,都会从原有的values序列里重新判断每个元素是否能够被2整除?这就是.NET 3.5中“Where”的延迟特性,如果您在这里没有意识到这点,就可能会产生重复计算,浪费了计算能力。

 解决方案

 解决这个问题的方法就是在合适的时候进行“强制计算”。例如:

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

 此时,在获得divideByTwo序列时,就会立即进行计算,这样在遍历后两者时就不会重复计算1,3,5等元素了。

0
相关文章