C
C#3mo ago
Jamie Brown

✅ Can't infer type arguments from usage

I was looking into writing an extension method to Flatten nested collections, as I always forget that SelectMany(i => i) exists (and also personally think it's ugly/non-intuitive). Said method looks like so:
public static IEnumerable<TValue> Flatten<TOuter, TInner, TValue>(this TOuter items) where TOuter : IEnumerable<TInner> where TInner : IEnumerable<TValue>
{
return items.SelectMany(item => item);
}
public static IEnumerable<TValue> Flatten<TOuter, TInner, TValue>(this TOuter items) where TOuter : IEnumerable<TInner> where TInner : IEnumerable<TValue>
{
return items.SelectMany(item => item);
}
Which is all fine and dandy, but if I go to use this like so:
List<List<int>> thing = [];
thing.Flatten();
List<List<int>> thing = [];
thing.Flatten();
The compiler will complain about being unable to infer the type arguments, so now I'd have to write this like so:
List<List<int>> thing = [];
thing.Flatten<List<List<int>>, List<int>, int>();
List<List<int>> thing = [];
thing.Flatten<List<List<int>>, List<int>, int>();
Which is even more hideous, and unintuitive to use than our good friend SelectMany() was in the first place. Is there a way (missing a constraint maybe?) to have this work without specifying the generic parameters? And if not why? It seems like the sort of thing that shouldn't be too difficult to reason about, but maybe this is another case where the brain is able to jump through hoops and connections the compiler is unable to.
4 Replies
Anton
Anton3mo ago
You either use the interface for the parameter, or write overloads for each concrete type You should probably take in IEnumerable since you're using linq already
TheBoxyBear
TheBoxyBear3mo ago
Also looking at the signature of SelectMany, it seems you could get away with a single generic argument. SelectMany has TSource and TResult but also takes a selector function where the return type could be different from the input.
SelectMany<TSource,TResult>(IEnumerable<TSource>, Func<TSource,IEnumerable<TResult>>)
SelectMany<TSource,TResult>(IEnumerable<TSource>, Func<TSource,IEnumerable<TResult>>)
As you're already calling it in the implementation, just use TValue for both TSource and TResult Good point though I'd bump a proposition for this being integrated into Linq or having an overload of SelectMany without parameters beyond the source collection.
Jamie Brown
Jamie BrownOP3mo ago
Actually this is one of the (all too common unfortunately) cases where I've made the problem more complex than it needs to be, you can just do:
public static IEnumerable<TItem> Flatten<TItem>(this IEnumerable<IEnumerable<TItem>> items)
{
return items.SelectMany(item => item);
}
public static IEnumerable<TItem> Flatten<TItem>(this IEnumerable<IEnumerable<TItem>> items)
{
return items.SelectMany(item => item);
}
And it will work without issue. I had it in my mind that the compiler would struggle to infer the type of the inner collection, but just goes to show I need to have a little more faith! !close
Accord
Accord3mo ago
Closed!
Want results from more Discord servers?
Add your server