Я столкнулся с типизированной ситуацией при попытке вызвать конкретный метод перегрузки, и теперь мне любопытно: почему следующее не вызывает ошибку компиляции?
var a = (Task<Task>) (Task) null; // fine.
var b = (Func<Task<Task>>) (Func<Task>) null; // fine.
var c = (Task<Task<Task>>) (Task<Task>) null; // compilation error.
I ожидал бы, что все эти примеры потерпят неудачу.
Ошибка из третьей строки (c
): Cannot cast expression of type 'System.Threading.Tasks.Task<System.Threading.Tasks.Task>' to type 'Task<Task<Task>>'
Task<T>
происходит отTask
.Ваша первая строка похожа на
(int) (object) 1
, что совершенно законно, потому что все ( в том числеint
) происходит изObject
.Вторая строка - это то же самое с контравариантностью в действии.
Третья строка терпит неудачу, потому что параметр
T
Task<T>
не настроен для ковариации (и, действительно, это не может быть потому, чтоTask<T>
- это класс, а не интерфейс или делегат). Он работает дляFunc<TResult>
, потому чтоFunc<TResult>
настроен для ковариации (т. Е. Объявлен какFunc<out TResult>
)). Задачи, возможно, были разработаны, чтобы запретить это по целям или они забыли сделать это.Task<T>
не выводится из ковариантного интерфейса / делегата.Func<out TResult>
.Task
является классом и поэтому не может иметь аргументы типа co / contra-varience. Это простая проблема иерархии заданий.Это экстраполяция ответа Майкла :
Для
a
:Task<T>
происходит отTask
, так что это нормально.Для
b
:Func
является делегат с ковариацией и поэтому неявно преобразуетTask
Для
c
:Task
нельзя использовать аргументы типа ковариации, так как это класс, иTask<Task<T>>
не получается изTask<T>
, поэтому это НЕ ОК.