如何在 C# 中最优雅地从匿名集合创建空集合?
如果我有一个由 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<>()?
If I have an anonymous type created by LINQ
var ans = from r in someList where someCondition(r) select new { r.a, r.b };
What is the best way to create an empty matching collection so I can move elements to the new collection:
var newans = ?
foreach (r in ans) { if (complicated(r)) newans.Add(r); }
Is there some way to use Enumerable.Empty<>()?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我不会使用 ans.Where(x => false).ToList() 版本来迭代所有元素,这是您不需要的;
ans.Take(0).ToList()
稍微好一些,因为当您查询内存中序列(LINQ to Objects)时,它实际上不会迭代整个列表。如果您使用 LINQ to SomethingElse,事情会有点问题,因为它实际上可能会执行类似
SELECT TOP(0) * FROM ...
或SELECT * FROM ... WHERE 1 的内容= 0 。不好。
因此,以下辅助方法将为您提供帮助:
话虽如此,如果您唯一的愿望是将
ans
的某些元素复制到新列表中,那么您最好使用or
如果您不需要实际的
List
和IEnumerable
就足够了,您可能完全省略ToList
。请注意,每次 foreach 此类可枚举时,Where(a => isComplicated(a))
都会一次又一次执行。对ToList
的调用可确保您只执行一次。如果您不希望这仅适用于
List
,而是更通用,那么事情会变得更加复杂。例如,该方法应该返回 ans 变量的静态类型还是其运行时类型?此外,除了盲目调用new C()
之外,许多集合不存在“默认”版本。但这一般行不通。无法调用new ReadOnlyCollection()
,因为它不存在(并且集合无论如何都会被密封,因此无法填充)。如果您使用 HasSet,您是否应该对空副本使用原始比较器而不是默认比较器?但是,对于最简单的情况,您可以尝试:
请注意,这是一个编译时解决方案,它返回变量类型的默认集合。如果可能的话。例如,
ans = from r in somesource select new { ... }
将具有静态类型IEnumerable<...>
。这里没有要创建的默认实例。此外,select
返回的运行时类型是 System.Core.dll 私有的,没有默认构造函数,并且无论如何都是只读的类似游标的类型。所以我对此的看法是:不要去那里。不要试图过度概括,坚持使用 ToEmptyList 等简单的解决方案,甚至更好,坚持使用简单的 LINQ 方法链接。
I wouldn't use the
ans.Where(x => false).ToList()
versions these iterate over all elements, which you don't need;ans.Take(0).ToList()
is a little better, as this one doesn't actually iterate over the entire list when you're querying over in-memory sequences (LINQ to Objects).If you are using LINQ to SomethingElse, things are a little problematic, because it might actually execute something like
SELECT TOP(0) * FROM ...
orSELECT * FROM ... WHERE 1 = 0
. Not good.So the following helper method will help you:
Having said that, if your only desire is to copy some elements of
ans
into a new list, you're better of withor
If you don't need an actual
List<T>
, and anIEnumerable<T>
is enough, you might get away with omitting theToList
altogether. Mind you, every time you foreach over such an enumerable,Where(a => isComplicated(a))
is executed again and again. The call toToList
makes sure you only execute it once.If you don't want this just for
List<T>
, but more general, things get a lot more complicated. For example, should the method return the static type of theans
variable or its runtime type? Also, there is no such thing as a "default" version of many collections, apart from blindly callingnew C()
. But that won't work generally. It's not possible to callnew ReadOnlyCollection<T>()
, because it doesn't exist (and the collection would be sealed anyway, so it cannot be filled). And if you're using a HasSet, shouldn't you be using the original's comparer for your empty copy rather than the default comparer?However, for the simplest case, you could try:
Note that this is a compile-time solution which returns a default collection of the variable's type. If at all possible. For instance
ans = from r in somesource select new { ... }
will have the static typeIEnumerable<...>
. There's no default instance here to create. Also, the runtime type returned byselect
is private to System.Core.dll, doesn't have a default constructor, and is a read-only cursor-like type anyway.So my opinion on this one is: don't go there. Don't try to over-generalize, stick to simple solutions like ToEmptyList or even better, stick to plain LINQ method chaining.
我还没有尝试过,但
似乎是一种生成正确类型的 IEnumerable 的方法(但不是可以“添加”到的集合 - 您可以将其添加到列表中)。
也就是说,如果您使用 LINQ,似乎不太可能有问题中的“foreach-if-.add”代码,只是
还是
没有?
I haven't tried it, but
seems like a way to generate the right type of IEnumerable (but not a collection you can 'add' to - you could ToList it).
That said, if you're using LINQ, seems unlikely to have the 'foreach-if-.add' code you have in the question, just
or
no?
var newans = ans.Where(false).ToList()
会做到这一点。无法使用
Enumerable.Empty
,因为无法显式传递匿名类型作为泛型参数。var newans = ans.Where(false).ToList()
will do it.Enumerable.Empty
can't be used because it's not possible to explicitly pass an anonymous type as a generic argument.使用 var newans = ans.ToList(); 会将每个枚举对象复制到新列表中。
Use
var newans = ans.ToList();
which will copy every enumerated object into a new list.