2015-06-23 11 views
34
class ResultBase {} 
class Result : ResultBase {} 

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

Der Compiler sagt mir, dass es nicht implizit Task<Result> in Task<ResultBase> konvertieren kann. Kann jemand erklären, warum das so ist? Ich hätte erwartet, dass die Kovarianz es mir ermöglicht, den Code auf diese Weise zu schreiben.Warum ist Task <T> nicht co-variant?

+2

Interfaces können nur kovariant oder kontravariant sein. Klasse ist immer invariant. Lesen Sie mehr unter: http://stackoverflow.com/questions/13107071/why-classes-that-implement-variant-interfaces-remain-invariant –

+0

Klassen sind in C# invariant. – Lee

+3

Von [dieser Antwort] (http://stackoverflow.com/questions/12204755/can-should-tasktresult-be-wrapped-in-ac-sharp-5-0-aaitable-which-is-covarian) scheint es, dass jemand hat [eine kovariante ITask Wrapper] (https://github.com/jam40jeff/ITask) dafür geschrieben. Man kann auch über [einen Vorschlag zur Umsetzung hier] abstimmen (https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/5754247-make-task-t-implement-covariant-interface-itask- O). –

Antwort

17

Nach someone who may be in the know ...

Die Begründung ist, dass der Vorteil der Kovarianz durch den Nachteil Unordnung aufgewogen wird (dh jeder würde eine Entscheidung treffen müssen, ob Aufgabe oder ITask zu verwenden, in jeder einzelne Ort in ihrem Code).

Es klingt für mich, als gäbe es keine sehr zwingende Motivation. ITask<out T> würde viele neue Überladungen erfordern, wahrscheinlich ziemlich viel unter der Haube (ich kann nicht bestätigen, wie die tatsächliche Basisklasse implementiert ist oder wie speziell es im Vergleich zu einer naiven Implementierung ist), aber viel mehr in Form dieser linq-like Erweiterungsmethoden.

Jemand anderes machte einen guten Punkt - die Zeit wäre besser zu verbringen class es kovariant und kontravariant. Ich weiß nicht, wie schwer das wäre, aber das klingt nach einer besseren Nutzung der Zeit für mich.

Auf der anderen Seite erwähnt jemand, dass es sehr cool wäre, eine echte yield return ähnliche Funktion in einer async Methode zur Verfügung zu haben. Ich meine, ohne Fingerfertigkeit.

+5

'async',' await' basiert auf der Existenz einer geeigneten 'GetAwaiter'-Methode, so dass sie bereits von der' Task'-Klasse abgekoppelt ist. – Lee

+0

Ich stehe bearbeitet/korrigiert –

6

Ich weiß, ich auf die Party zu spät komme, aber hier ist eine Erweiterung Methode, die ich für dieses fehlende Feature zu berücksichtigen habe mit:

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

Auf diese Weise können Sie einfach tun:

class ResultBase {} 
class Result : ResultBase {} 

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