web-dev-qa-db-ja.com

Task <T>が共変ではないのはなぜですか?

class ResultBase {}
class Result : ResultBase {}

Task<ResultBase> GetResult() {
    return Task.FromResult(new Result());
}

コンパイラーは、Task<Result>Task<ResultBase>に暗黙的に変換できないことを教えてくれます。これがなぜなのか誰かが説明できますか?この方法でコードを記述できるようにするために、共分散を期待していました。

52
chosenbreed37

知っている人 ...によると.

正当化は、共分散の利点が乱雑さの欠点に勝るということです(つまり、誰もがコードのすべての場所でTaskまたはITaskを使用するかどうかの決定をしなければならないでしょう)。

どちらにしても、非常に説得力のある動機はないように思えます。 ITask<out T>は多くの新しいオーバーロードを必要としますが、おそらく実際にはかなりの部分(実際の基本クラスがどのように実装されているか、または単純な実装と比較してどのように特別であるかについては証明できません)ですが、これらの形式でさらに多くのlinqのような拡張メソッド。

他の誰かが良い点を作りました-classesを共変と反変にするのに費やす時間はもっと良いでしょう。それがどれほど難しいかはわかりませんが、それは私にとって時間のより良い使い方のように思えます。

一方、誰かが本当のyield returnのような機能はasyncメソッドで使用できます。つまり、手抜きなし。

21

私はパーティーに遅れていることに気づきましたが、この欠けている機能を説明するために私が使用してきた拡張メソッドは次のとおりです。

/// <summary>
/// Casts the result type of the input task as if it were covariant
/// </summary>
/// <typeparam name="T">The original result type of the task</typeparam>
/// <typeparam name="TResult">The covariant type to return</typeparam>
/// <param name="task">The target task to cast</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task<TResult> AsTask<T, TResult>(this Task<T> task) 
    where T : TResult 
    where TResult : class
{
    return task.ContinueWith(t => t.Result as TResult);
}

このようにしてあなたはただ行うことができます:

class ResultBase {}
class Result : ResultBase {}

Task<ResultBase> GetResult() 
{
    return Task.FromResult(new Result()).AsTask<Result, ResultBase>();
}
9
Sergio0694