Reputation: 1461
I have noticed that all of the Linq extension methods Where
, Any
, Take
etc. all return IEnumerable
. For my application it would be a lot nicer if these returned an instance of the same type as the one I am calling on.
So for instance, if I call myList.Where(...)
on a LinkedList
, I would like to get a LinkedList
back, on a List
i'd like a List
back, etc.
This becomes more annoying when I have a type like
class MyType
{
public string MyField;
}
class MyCollection : List<MyType>
{
// Some useful stuff in here
}
in which case I would like to be able to do
MyCollection source = .......
MyCollection filtered = source.Where(obj => obj.MyField != null);
Sadly, this won't work out of the box. However, I have come close, using the following code
public static ListType Filtered<ListType, ElementType>(this ListType @this, Predicate<ElementType> filter) where ListType : class, ICollection<ElementType>, new()
{
// Arguments checks omitted
// Create return instance
var filtered = new ListType();
// Apply filter for each element
foreach (var item in @this)
{
if (filter(item))
filtered.Add(item);
}
return filtered;
}
I can do (Notice the stronly typed delegate)
MyCollection source = .......
MyCollection filtered = source.Filtered((MyType obj) => obj.MyField != null);
So my question is: Why do I need the second generic argument in Filter<ListType, ElementType>
, when I specify where ListType : ICollection<ElementType>
? I understand I cannot use any generic types in where
that I haven't added to the method declaration, but why is this? Is there a nicer way of doing this that I have overlooked?
Upvotes: 1
Views: 253
Reputation: 1499730
Well, you need two type parameters because you've got two types involved: the collection type, and the element type. There's no way of saying, it's got to implement IFoo<T>
for some kind of T
, but we don't care what that T
is.
You're also using both types elsewhere in the declaration: ListType
as the return type, and ElementType
in the predicate declaration. (This method declaration would be easier to understand if you'd followed .NET naming conventions and called them something like TCollection
and TElement
, by the way.)
You're also using both types within the method body: item
is statically typed to be ElementType
, and filtered
is statically typed to be ListType
.
It's hard to see how you would express all of this without both type parameters. Given that type inference lets you call the method without being explicit, it's not clear to me why this is a problem...
Upvotes: 3