代码之家  ›  专栏  ›  技术社区  ›  Ray Booysen

产量方法中的垃圾收集

  •  1
  • Ray Booysen  · 技术社区  · 17 年前

    假设我有这样一种方法(从Jon Skeet之前的SO答案中盗取):

    public static IEnumerable<TSource> DuplicatesBy<TSource, TKey>
        (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        HashSet<TKey> seenKeys = new HashSet<TKey>();
        foreach (TSource element in source)
        {
            // Yield it if the key hasn't actually been added - i.e. it
            // was already in the set
            if (!seenKeys.Add(keySelector(element)))
            {
                yield return element;
            }
        }
    }
    

    在这个方法中,我有一个HashSet,用于保存已看到的键。如果我在这样的事情中使用这个方法。

    List<string> strings = new List<string> { "1", "1", "2", "3" };
    List<string> somewhatUniques = strings.DuplicatesBy(s => s).Take(2);
    

    2 回复  |  直到 17 年前
        1
  •  2
  •   Hans Passant    17 年前

    编译器生成一个隐藏类来实现此代码。它有一个超级秘密的名字:“d_uu0`2”。Seenkey和源变量成为该类的字段,确保它们不能被垃圾收集,除非收集类对象。

    该类实现IEnumerator<&燃气轮机;接口,使用迭代器的客户端代码使用该接口调用MoveNext()方法。正是接口引用使类对象保持活动状态。这让它的田地生机勃勃。一旦客户机代码完成foreach循环,接口引用就会消失,从而允许GC清理所有内容。

    使用Ildasm.exe或Reflector亲自查看。它还将让您了解语法糖的隐藏成本。迭代器并不便宜。

        2
  •  1
  •   mqp    17 年前

    . 显然,这是不可能的。

    因此,如果您的迭代器中有很多昂贵的状态,并且您对其进行了很长时间的迭代,那么您可能希望不使用yield-return,或者通过调用ToArray()之类的函数立即计算整个枚举,然后查看它。

    编辑 :因此,在回答您的最后一个问题——如何确保它得到处理——如果您在其上使用LINQ或foreach构造,则无需执行任何特殊操作,因为它们通过其通常的魔法自行处理。如果要手动获取枚举数,请确保在完成后对其调用Dispose(),或将其放入using块中。

    推荐文章