简体   繁体   中英

Are Task and Task<T> Interchangeable?

I ran across a typing situation while attempting to invoke a specific method overload, and now I'm curious: why does the following not induce a compilation error?

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 would expect all of these examples to fail.

Error from the third line ( c ): Cannot cast expression of type 'System.Threading.Tasks.Task<System.Threading.Tasks.Task>' to type 'Task<Task<Task>>'

Task<T> derives from Task .

Your first line is sort of like doing (int) (object) 1 , which is perfectly legal because everything (including int ) derives from Object .

Your second line is the same thing with contravariance in action.

The third line fails because the T parameter of Task<T> is not set up for covariance (and indeed, it cannot be because Task<T> is a class, not an interface or delegate). It works for Func<TResult> because Func<TResult> is set up for covariance (ie, it's declared as Func<out TResult> ). Tasks may have been designed to disallow this on purposes, or they forgot to do it.

This is an extrapolation of Michael's answer :

For a : Task<T> derives from Task , so this is OK.

For b : Func is a delegate with covariance, and therefore is implicitly converting Task to Task<T> on our behalf. Func is unaltered, and therefore this is OK.

For c : Task cannot use covariant type arguments since it is a class, and Task<Task<T>> does not derive from Task<T> , therefore this is NOT OK.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM