有没有办法记住或物化IEnumerable?

时间:2011-01-07 00:53:40

标签: c# linq

当给定d时,您可能正在处理一个固定序列,如列表或数组,AST将枚举一些外部数据源,甚至是某些现有集合上的AST。有没有办法安全地“实现”枚举,以便像foreach,count等枚举操作每次都不执行AST?

我经常使用.ToArray()创建此表示,但如果底层存储已经是列表或其他固定序列,那么这似乎是浪费复制。如果我能做的话会很好

var enumerable = someEnumerable.Materialize();

if(enumberable.Any() {
  foreach(var item in enumerable) {
    ...
  }
} else {
  ...
}

不必担心.Any()foreach尝试枚举序列两次而不会不成功地复制可枚举。

3 个答案:

答案 0 :(得分:6)

足够简单:

public static IList<TSource> Materialize<TSource>(this IEnumerable<TSource> source)
{
    if (source is IList<TSource>)
    {
        // Already a list, use it as is
        return (IList<TSource>)source;
    }
    else
    {
        // Not a list, materialize it to a list
        return source.ToList();
    }
}

答案 1 :(得分:6)

与托马斯的答案相同,根据我的说法好一点:

public static ICollection<T> Materialize<T>(this IEnumerable<T> source)
{
    if (source == null) 
        return null;

    return source as ICollection<T> ?? source.ToList();
}

请注意,如果它是有效的集合类型,则会倾向于返回现有集合本身,否则会生成新集合。虽然两者略有不同,但我认为这不是一个问题。

答案 2 :(得分:3)

查看我几年前撰写的这篇博文:http://www.fallingcanbedeadly.com/posts/crazy-extention-methods-tolazylist

在其中,我定义了一个名为ToLazyList的方法,可以有效地完成您正在寻找的内容。

正如所写的那样,它最终会生成输入序列的完整副本,尽管你可以调整它以便IList的实例不会被包装在LazyList中,这会阻止这种情况发生(但是,这个动作会随身携带的假设是你得到的任何IList都已经被有效地记忆了。)