Tengo este código:

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);
}    

Por qué esta línea se compila bien: return Task.Run(() => x); mientras que return Task.Run(() => lst); da el error del compilador:

No se puede convertir implícitamente el tipo 'System.Threading.Tasks.Task ' a 'System.Threading.Tasks.Task '

Creo que dado que List<string> es un IEnumerable<string>, no debería tener que establecer explícitamente x. Quiero decir, el compilador sabe que lst es un IEnumerable<string>.

Creo que debo estar perdiendo algo fundamental aquí.

c#
2
Tom Baxter 27 ago. 2020 a las 15:52

2 respuestas

La mejor respuesta

Porque C # primero intenta determinar el tipo de su expresión Task.Run(() => lst) y luego verifica si es un tipo de retorno adecuado para su método.

  1. lst es una List<string>.
  2. Por lo tanto, () => lst es una lambda con un valor de retorno de List<string>.
  3. Por lo tanto, Task.Run<TResult>(Func<TResult>) llamado con () => lst infiere List<string> como su parámetro de tipo TResult.
  4. Por tanto, Task.Run(() => lst) es de tipo Task<List<string>>.

Luego intente usar un valor de tipo Task<List<string>> como el valor de retorno de un método que devuelve un Task<IEnumerable<string>>. Task<TResult> no es covariante, por lo tanto, obtiene un error en tiempo de compilación.

(Como comentario aparte: tener una interfaz ITask<out TResult> covariante solucionaría esto, pero el equipo de desarrollo de .NET decidió no hacerlo .)

Sí, el compilador podría comprobar cómo está utilizando la expresión creada en el paso 4 y probar diferentes interfaces de lst para que se ajuste, pero esa es una función que C # (actualmente) no lo tiene (y que, en mi opinión puramente personal, probablemente no vale la pena implementar y mantener debido a su baja relación costo-beneficio).


Para resolver esto, puede proporcionar un tipo diferente en el paso 2, emitiendo explícitamente el tipo de retorno:

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

O declarando explícitamente el tipo de expresión lambda:

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

O puede anular la inferencia automática en el paso 3 con un argumento de tipo explícito:

return Task.Run<IEnumerable<string>>(() => lst);
3
Heinzi 27 ago. 2020 a las 13:36

El problema es que está utilizando diferentes tipos de datos aquí (List<T> frente a IEnumerable<T>). Puede solucionar el problema con el uso de conversión de tipos como:

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

1
Deni Juric 27 ago. 2020 a las 13:02