如何判断一个IEnumerable是否受延迟执行的影响?

20 浏览
0 Comments

如何判断一个IEnumerable是否受延迟执行的影响?

我一直认为,如果我在LINQ to objects的上下文中使用Select(x=> ...),那么新的集合会立即被创建并保持静态。我不太确定为什么我会有这种假设,这是一个非常糟糕的假设,但我确实有这种想法。我经常在其他地方使用.ToList(),但在这种情况下通常不使用。

以下代码演示了即使是简单的“Select”也会受到延迟执行的影响:

var random = new Random();
var animals = new[] { "cat", "dog", "mouse" };
var randomNumberOfAnimals = animals.Select(x => Math.Floor(random.NextDouble() * 100) + " " + x + "s");
foreach (var i in randomNumberOfAnimals)
{
    testContextInstance.WriteLine("There are " + i);
}
foreach (var i in randomNumberOfAnimals)
{
    testContextInstance.WriteLine("And now, there are " + i);
}

这将输出以下内容(每次迭代集合时都会调用随机函数):

There are 75 cats
There are 28 dogs
There are 62 mouses
And now, there are 78 cats
And now, there are 69 dogs
And now, there are 43 mouses

我的代码中有很多地方,我在类的成员中使用IEnumerable。通常对我来说,这不会引起问题,但最近我在代码中发现了一些地方,它不仅仅是性能问题。

为了检查是否犯了这个错误,我尝试检查特定的IEnumerable是否属于IQueryable类型。我认为这会告诉我集合是否“延迟”。结果发现,上述Select操作符创建的枚举器是System.Linq.Enumerable+WhereSelectArrayIterator``[System.String,System.String]类型,而不是IQueryable类型。

我使用Reflector来查看这个接口继承自哪里,结果发现它没有继承任何表明它是“LINQ”的东西 - 所以无法根据集合类型进行测试。

现在我很高兴到处使用.ToArray(),但我希望有一种机制来确保这个问题不会再次发生。Visual Studio似乎知道如何做到这一点,因为它会显示一个关于“展开结果视图将评估集合”的消息。

我想到的最好的方法是:

bool deferred = !object.ReferenceEquals(randomNumberOfAnimals.First(),
                                        randomNumberOfAnimals.First());

编辑:这只适用于使用“Select”创建新对象的情况,而且不是通用解决方案。不过,在任何情况下我都不建议使用它!这只是一个玩笑性质的解决方案。

0