如果我有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<>()?
答案 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();
将每个枚举对象复制到一个新列表中。