如何在C#中最优雅地从匿名集合中创建一个空集合?

时间:2010-05-20 22:46:53

标签: c# linq collections

如果我有LINQ创建的匿名类型

var ans = from r in someList where someCondition(r) select new { r.a, r.b };

创建空匹配集合的最佳方法是什么,以便我可以将元素移动到新集合:

var newans = ?
foreach (r in ans) { if (complicated(r)) newans.Add(r); }

有没有办法使用Enumerable.Empty<>()?

4 个答案:

答案 0 :(得分:6)

我不会使用ans.Where(x => false).ToList()版本迭代所有你不需要的元素; ans.Take(0).ToList()稍好一些,因为当你查询内存中的序列(LINQ to Objects)时,这个实际上并没有迭代整个列表。

如果你正在使用LINQ to SomethingElse,那么事情就有点问题,因为它实际上可能会执行SELECT TOP(0) * FROM ...SELECT * FROM ... WHERE 1 = 0之类的事情。不好。

因此以下帮助方法可以帮助您:

public static class AnonymousTypeExtensions
{
    public static List<T> ToEmptyList<T>(this IEnumerable<T> source)
    {
        return new List<T>();
    }
}

var newans = ans.ToEmptyList();

话虽如此,如果您唯一的愿望是将ans的某些元素复制到新列表中,那么您最好使用

var newans = (from a in ans where isComplicated(a) select a).ToList();

var newans = ans.Where(a => isComplicated(a)).ToList();

如果您不需要实际的List<T>,并且IEnumerable<T>就足够了,您可能会完全省略ToList。请注意,每当你对这样一个可枚举的人进行预告时,Where(a => isComplicated(a))会一次又一次地被执行。对ToList的调用确保您只执行一次。


如果您不希望这只是List<T>,但更一般,事情会变得复杂得多。例如,该方法是否应返回ans变量的静态类型或其运行时类型?此外,除了盲目地调用new C()之外,没有许多集合的“默认”版本。但这不会普遍起作用。无法调用new ReadOnlyCollection<T>(),因为它不存在(并且集合将被密封,因此无法填充)。如果您使用的是HasSet,那么您是否应该使用原始的比较器作为空副本而不是默认的比较器?

但是,对于最简单的情况,您可以尝试:

public static class CollectionExtensions
{
    public static TCollection AsEmpty<TCollection>(this TCollection source)
        where TCollection : ICollection, new()
    {
        return new TCollection();
    }
}

请注意,这是一个编译时解决方案,它返回变量类型的默认集合。如果可能的话。例如,ans = from r in somesource select new { ... }将具有静态类型IEnumerable<...>。这里没有要创建的默认实例。此外,select返回的运行时类型对System.Core.dll是私有的,没有默认构造函数,并且无论如何都是一个只读的游标类型。

所以我对这一点的看法是:不要去那里。不要试图过度概括,坚持使用像ToEmptyList这样的简单解决方案,甚至更好,坚持使用简单的LINQ方法链接。

答案 1 :(得分:3)

我没试过,但是

var newans = from r in ans where false select r;

似乎是一种生成正确类型的IEnumerable的方法(但不是你可以'添加'的集合 - 你可以将它列出来)。

那就是说,如果你使用LINQ,似乎不太可能有问题中的'foreach-if-.add'代码,只是

var newans = from r in ans where isComplicated(r) select r;

var newans = ans.Where(isComplicated);

没有

答案 2 :(得分:1)

var newans = ans.Where(false).ToList()会这样做。

无法使用

Enumerable.Empty,因为无法将匿名类型显式传递为泛型参数。

答案 3 :(得分:0)

使用var newans = ans.ToList();将每个枚举对象复制到一个新列表中。