2

考虑以下代码块:

using (FileStream fs = new FileStream(@"C:\bad_records.txt", 
                                      FileMode.Create, 
                                      FileAccess.Write))
{
    var badEnumerable = _cache.Where(kvp => !kvp.Value.Item1);

    fs.WriteLine(string.Format("BAD  RECORDS ({0})", badEnumerable.Count()));
    fs.WriteLine("==========");

    foreach (var item in badEnumerable)
    {
        fs.WriteLine(string.Format("{0}: {1}", item.Key, item.Value.Item2));
    }
}

where_cache定义如下:

static Dictionary<string, Tuple<bool, string, string>> _cache;

我是否将这个可枚举迭代了两次?一次与Count()一次与foreach

4

5 回答 5

8

是的,您正在迭代可枚举两次。

一个简单的测试方法是使用这样的辅助方法:

private static int count = 0;
public static IEnumerable<T> CountIterations<T>(IEnumerable<T> sequence)
{
    count++;
    //or some other debug type logging
    Console.WriteLine("Iterated {0} times.", count);
    foreach(var item in sequence)
        yield return item;
}
于 2013-04-23T14:53:09.233 回答
3

你使用你的字典_cache作为IEnumerable。 Where 方法已延迟执行。 所以你枚举它两次: in和 in 。KeyValuePair

Countforeach

您可以将其更改为:
var badEnumerable = _cache.Where(kvp => !kvp.Value.Item1).ToArray();
fs.WriteLine(string.Format("BAD RECORDS ({0})", badEnumerable.Length));

于 2013-04-23T14:55:27.623 回答
2

是的, the.Count()和 theforeach都将导致_cache被枚举两次并针对Where原因中的谓词进行验证。

至于这是否是一个问题,取决于许多事情:

  1. 内存中的所有值是否都已_cache在内存中,或者它是否正在查询底层源(例如数据库)。
  2. 查询的集合中有多少项目以及比较的成本是多少。
  3. 可以多次安全地枚举源。

例如,如果 in_cache的值已经在内存中并且谓词是一个简单的布尔属性比较,那么枚举缓存两次可能更有效并且不会增加额外的内存开销,而添加 a.ToList()仍然会导致 2 个枚举(其中一个_cache和一个列表)但是谓词检查只会发生一次(在.ToList()调用中)并且foreach将有更少的对象要枚举,但您将添加额外列表的额外内存开销。

如果缓存来自数据库,那么在.ToList()之后添加 a 的内存开销.Where()几乎肯定会比对数据库执行 2 个单独的查询要好。

于 2013-04-23T15:07:46.913 回答
1

简短的回答是肯定的。

根据 的基础类型badEnumerable,它可能会被枚举两次。这是由于所谓的“延迟执行”。延迟执行意味着您的 LINQ 查询“直到在 foreach 或 For Each 循环中迭代查询变量”(MSDN)才真正执行。您的foreach语句显然是对变量的迭代,并且Enumerable.Count()还执行迭代(在这种情况下)。

但在某些情况下,这不会导致两次迭代。当badEnumerable它实际上是 的子类ICollection时会发生这种情况。在这种情况下,调用.Count()实际上引用了基础.Count属性,而不是枚举它。

由于badEnumerableis a Dictionary<TKey, TValue>,并且因为调用Enumerable.Where()返回一个泛型IEnumerable(不是 an ICollection),所以您的特定情况不会遇到这种情况,并且会迭代两次。

于 2013-04-23T15:25:30.817 回答
0

是的,为避免重复两次,请使用 aList 您可以在初始迭代后打印计数。另一种解决方案是保存循环中写入的文本并在打印计数后打印。

编辑更正:

using (FileStream fs = new FileStream(@"C:\bad_records.txt", FileMode.Create, FileAccess.Write))
{
    var badEnumerable = _cache.Where(kvp => !kvp.Value.Item1);

    int count = 0;
    foreach (var item in badEnumerable)
    {
        count++;
        Console.WriteLine(string.Format("{0}: {1}", item.Key, item.Value.Item2));
    }

    Console.WriteLine("==========");
    Console.WriteLine(string.Format("BAD  RECORDS ({0})", count));
}
于 2013-04-23T14:54:40.627 回答