views:

106

answers:

4

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<>()?

+3  A: 

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?

Brian
+1  A: 

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.

Stephen Cleary
A: 

Use var newans = ans.ToList(); which will copy every enumerated object into a new list.

Julien Lebosquain
+3  A: 

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.

Ruben
That is very nice - seems like the kind of thing the language should have, but can it be genericized to handle when the var ans is not a List<T>?
NetMage
This works on any IEnumerable<T> (including LINQ queries), for any T. Or I might not fully understand your question.
Ruben
It will convert any IEnumerable<T> to an empty List<T>, but how about creating an empty X<T> for any X? What about var v = Default(ans)?
NetMage
I've elaborated on that part now. Simply put: don't do that :-)
Ruben
@Ruben: +1, great answer. Addresses some coding style suggestions that transcend OP's question.
maxwellb