How can I create an empty collection from an anonymous collection most elegantly in C#?
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<>()?
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 ...
or SELECT * FROM ... WHERE 1 = 0
. Not good.
So the following helper method will help you:
public static class AnonymousTypeExtensions
{
public static List<T> ToEmptyList<T>(this IEnumerable<T> source)
{
return new List<T>();
}
}
var newans = ans.ToEmptyList();
Having said that, if your only desire is to copy some elements of ans
into a new list, you're better of with
var newans = (from a in ans where isComplicated(a) select a).ToList();
or
var newans = ans.Where(a => isComplicated(a)).ToList();
If you don't need an actual List<T>
, and an IEnumerable<T>
is enough, you might get away with omitting the ToList
altogether. Mind you, every time you foreach over such an enumerable, Where(a => isComplicated(a))
is executed again and again. The call to ToList
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 the ans
variable or its runtime type? Also, there is no such thing as a "default" version of many collections, apart from blindly calling new C()
. But that won't work generally. It's not possible to call new 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:
public static class CollectionExtensions
{
public static TCollection AsEmpty<TCollection>(this TCollection source)
where TCollection : ICollection, new()
{
return new TCollection();
}
}
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 type IEnumerable<...>
. There's no default instance here to create. Also, the runtime type returned by select
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.
I haven't tried it, but
var newans = from r in ans where false select r;
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
var newans = from r in ans where isComplicated(r) select r;
or
var newans = ans.Where(isComplicated);
no?
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.
Use var newans = ans.ToList();
which will copy every enumerated object into a new list.
精彩评论