简体   繁体   中英

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM