Tom Baxter
Tom Baxter

Reputation: 2208

Why Does it Not Compile?

I have this code:

static List<string> lst = new List<string>();

public static Task<IEnumerable<String>> GetStrings() {
    IEnumerable<String> x = lst;
    
    // This line below compiles just fine.
    // return Task.Run(() => x);
    
    // This line gives a compiler error:
    // Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.List<string>>' to
    // 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<string>>'
    return Task.Run(() => lst);
}    

Why does this line compile just fine: return Task.Run(() => x); while return Task.Run(() => lst); gives the compiler error:

Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.List>' to 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable>'

I would think that since List<string> is an IEnumerable<string>, I should not have to explicitly set x. I mean, the compiler knows lst is an IEnumerable<string>.

I think I must be missing something fundamental here.

Upvotes: 1

Views: 166

Answers (2)

Heinzi
Heinzi

Reputation: 172448

Because C# first tries to determine the type of your expression Task.Run(() => lst) and then checks whether it is a suitable return type for your method.

  1. lst is a List<string>.
  2. Thus, () => lst is a lambda with a return value of List<string>.
  3. Thus, Task.Run<TResult>(Func<TResult>) called with () => lst infers List<string> as its type parameter TResult.
  4. Thus, Task.Run(() => lst) is of type Task<List<string>>.

You then try to use a value of type Task<List<string>> as the return value of a method returning a Task<IEnumerable<string>>. Task<TResult> is not covariant, hence, you get a compile-time error.

(As an aside: Having a covariant ITask<out TResult> interface would fix this, but the .NET development team decided against it.)

Yes, the compiler could check how you are using the expression created in step 4 and try different interfaces of lst to make it fit, but that's a feature that C# (currently) does not have (and which, in my purely personal opinion, is probably not worth implementing and maintaining due to its low cost-benefit ratio).


To solve this, you can either provide a different type in step 2, by explicitly casting the return type:

return Task.Run(() => (IEnumerable<string>)lst);

or by explicitly declaring the type of the lambda expression:

return Task.Run(new Func<IEnumerable<string>>(() => lst));

or you could override the automatic inference in step 3 with an explicit type argument:

return Task.Run<IEnumerable<string>>(() => lst);

Upvotes: 5

Adriatic
Adriatic

Reputation: 1287

The problem is that you are using different data types here (List<T> vs. IEnumerable<T>). You can fix the issue with using type casting like:

Task.Run(() => (IEnumerable<string>)lst);

Upvotes: 1

Related Questions