2

I'm struggling to wrap my brain around type inference within an asynchronous extension method I've adopted from this answer.

public static class EnumerableExtensions
{
  public static async Task<IEnumerable<T1>> SelectAsync<T, T1>(
    this IEnumerable<T> enumeration,
    Func<T, Task<T1>> func)
  {
    return await Task.WhenAll(enumeration.Select(func));
  }

  public static async Task<IEnumerable<T1>> SelectManyAsync<T, T1>(
    this IEnumerable<T> enumeration,
    Func<T, Task<IEnumerable<T1>>> func)
  {
    return (await enumeration.SelectAsync(func)).SelectMany(x => x);
  }
}

I'm making use of the extension methods as follows:

var allChildren = await parents.SelectManyAsync(parent => ReturnsChildrenAsync(parent));

parents is of type IList<Parent>

ReturnsChildrenAsync() returns a Task<IList<Child>>

But I'm getting the following error:

The type arguments for method 'EnumerableExtensions.SelectManyAsync<T, T1>(IEnumerable<T>, Func<T, Task<IEnumerable<T1>>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

It seems that T1 cannot be inferred. Why can't this be derived from Task<IList<Child>>?

Chris Pickford
  • 8,642
  • 5
  • 42
  • 73
  • 2
    Because `Task>` != `Task>`? – CodeCaster Jun 19 '18 at 12:25
  • Doesn't `IList` implement `IEnumerable` though? – Chris Pickford Jun 19 '18 at 12:26
  • You should await `ReturnsChildrenAsync` in the lambda expression. – Gert Arnold Jun 19 '18 at 12:34
  • @CodeCaster You're right, when changing to use `Func>> func` it works. Is there a way of inferring the nested type arg when dealing with interfaces like this? – Chris Pickford Jun 19 '18 at 12:35
  • @GertArnold `ReturnsChildrenAsync` needs to return a task as the `Task.WhenAll` handles the await, at least that's how I'm understanding it. – Chris Pickford Jun 19 '18 at 12:37
  • When you change the return of `ReturnsChildrenAsync()` to `Task>` it also works. Seems kind of cleaner. – bommelding Jun 19 '18 at 12:43
  • @bommelding Unfortunately `ReturnsChildrenAsync()` refers to a library method outside of my control. I believe the root issue here is that an `IList` can't be inferred as an `IEnumerable`, which doesn't make a lot of sense to me. – Chris Pickford Jun 19 '18 at 12:45
  • 1
    Well, find one of those Eric Lippert answers with tigers and giraffes about co- and contravariance. – bommelding Jun 19 '18 at 12:50
  • Could you try try `SelectManyAsync`? – Kacper Jun 19 '18 at 13:25
  • 1
    I think the issue is that `Task<>` was written before [Variance in Generic Interfaces](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/variance-in-generic-interfaces) was added in .Net 4, it simply isn't in the framework. You could open a bug. – NetMage Jun 19 '18 at 17:55

0 Answers0