在Visual Studio中,ReSharper警告:“可能多次枚举IEnumerable”,代码如下:
static void Main(string[] args)
{
IEnumerable<string> items = Test2();
foreach (var item in items)
{
Console.WriteLine(item);
}
var newitems = new StringBuilder();
foreach (var item in items)
{
newitems.Append(item);
}
}
private static IEnumerable<string> Test2()
{
string[] array1 = { "1", "2", "3" };
return array1;
}
我希望Test2方法将被调用两次,但它被调用一次。
我错过了什么?
答案 0 :(得分:6)
它只被调用一次,因为Test2()
实际上返回string []
,这也是IEnumerable<string>.
此string []
数组仍由items
引用,因此每次使用items
时,您只需重新使用该数组。
您期望的案例是Test2()
的实施,iterator block:
private static IEnumerable<string> Test2()
{
string[] array1 = { "1", "2", "3" };
foreach (var str in array1)
{
yield return str;
}
}
答案 1 :(得分:5)
看一下这个例子:
void Main()
{
IEnumerable<int> items = Test2();
foreach (var item in items)
{
Console.WriteLine(item);
}
var newitems = new StringBuilder();
foreach (var item in items)
{
newitems.Append(item);
}
}
IEnumerable<int> Test2()
{
Console.WriteLine("Test2 called");
return GetEnum();
}
IEnumerable<int> GetEnum()
{
for(var i = 0; i < 5; i ++)
{
Console.WriteLine("Doing work...");
Thread.Sleep(50); //Download some information from a website, or from a database
yield return i;
}
}
想象一下return GetEnum();
是return new int[] { 1, 2, 3 }
现在,对于数组,多次迭代它们并不一定是件坏事。在你的情况下,你可以在一个循环中完成工作,但这不是resharper警告你的原因。它会警告你,因为Test2()
可能会在每次迭代时返回可以正常工作的惰性枚举。
如果您运行上面的代码,您将获得此输出:
Test2 called
Doing work...
0
Doing work...
1
Doing work...
2
Doing work...
3
Doing work...
4
Doing work...
Doing work...
Doing work...
Doing work...
Doing work...
请注意Test2
本身只被调用一次,但是枚举被迭代两次(并且工作完成了两次!)。
你可以写下:
来避免这种情况var items = Test2().ToList();
将立即评估可枚举并将其放入列表中。在这种情况下,工作只需一次。
答案 2 :(得分:0)
IEnumerable<T>
是一个具有枚举器的接口,每次要访问数据集(foreach循环)时都会调用该枚举器。 Resharper警告您,如果您的数据没有被排序,并且您多次在数据集上调用此枚举器,那么运行时可能需要多次遍历您的集合,这会加载并减慢执行时间。
为了避免您首先将数据集转换为有序集合:例如:在你的items变量上调用.ToArray()或.ToList()。
答案 3 :(得分:0)
正如许多人所指出的,这一警告的目的是指出昂贵的行动可能不止一次发生。发生这种情况是因为ReSharper看到你的方法返回一个可能导致延迟评估的IEnumerable,如果你使用yield返回或大多数LINQ方法。
当ReSharper可以确定您正在迭代的东西是一个集合时,它会停止关于多次评估的警告。您可以通过两种方式向ReSharper提供该信息。
IList<string>
foreach
添加System.Diagnostics.Debug.Assert(items is
IList<string>);
如果对返回的ToList()
使用IEnumerable<string>
,ReSharper也会知道你正在迭代一个集合,但你也会创建一个不必要的临时列表(你已经有了一个数组),付费构建新列表的时间和内存成本。