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

Arn*_*sen 8 c# linq

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

我经常用来.ToArray()创建这个表示,但如果底层存储已经是一个列表或其他固定序列,那似乎就是浪费了复制.如果我能这样做会很好

var enumerable = someEnumerable.Materialize();

if(enumberable.Any() {
  foreach(var item in enumerable) {
    ...
  }
} else {
  ...
}
Run Code Online (Sandbox Code Playgroud)

不必担心,.Any()foreach尝试枚举序列两次,没有它不成功复制可枚举.

naw*_*fal 8

和托马斯的回答一样,根据我的说法,这个问题要好一点:

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

    return source as ICollection<T> ?? source.ToList();
}
Run Code Online (Sandbox Code Playgroud)

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


Tho*_*que 7

很容易:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这与Linq.ToList()实现略有不同,Linq.ToList()实现似乎总是返回一个新副本,因此对结果的更改不会更改原始副本.具体写作将根据输入的类型,有时返回一个副本,有时返回原始 - 所以结果的更改有时会改变原始. (6认同)
  • 这是一个很好的方法.我认为最好还是返回一个`IEnumerable <T>`,并检查`ICollection`和`ICollection <T>`. (3认同)
  • 或者在一行中,`返回源为IList <TSource> ?? source.ToList();` (3认同)