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

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

Kompilator mówi, że nie może domyślnie konwertować Task<Result> na Task<ResultBase>. Czy ktoś może wyjaśnić, dlaczego tak jest? Spodziewałbym się współzmienności, aby umożliwić mi napisanie kodu w ten sposób.Dlaczego zadanie <T> nie jest co-wariantem?

+2

Infterfaces może być tylko kowariantny lub contravariant. Klasa zawsze jest niezmienna. Przeczytaj więcej na: http://stackoverflow.com/questions/13107071/why-classes-that-implement-variant-interfaces-remain-invariant –

+0

Klasy są niezmienne w C#. – Lee

+3

Z [tej odpowiedzi] (http://stackoverflow.com/questions/12204755/can-should-tasktresult-be-wrapped-in-ac-sharp-5-0a-awaitable-which-is-covarian) wydaje się, że ktoś napisał [opakowanie Covarari ITask ] (https://github.com/jam40jeff/ITask) dla niego. Można również głosować na [sugestię, aby wprowadzić go tutaj] (https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/5754247-make-task-t-implement-covariant-interface-itask- o). –

Odpowiedz

17

Według someone who may be in the know ...

Uzasadnieniem jest to, że zaletą kowariancji jest niwelowana przez wadę bałaganu (czyli każdy będzie musiał podjąć decyzję o tym, czy korzystania zadanie lub w iTask każdy pojedynczy miejsce w swoim kodzie).

Wydaje mi się, że nie ma zbyt silnej motywacji w żaden sposób. ITask<out T> wymagałoby wielu nowych przeciążeń, prawdopodobnie całkiem pod maską (nie mogę potwierdzić, w jaki sposób jest zaimplementowana faktyczna klasa bazowa lub jak wyjątkowe jest to porównanie do naiwnej implementacji), ale o wiele bardziej w postaci tych linq podobnych do metody rozszerzenia.

Ktoś inny trafił w dobry punkt - lepiej byłoby poświęcić czas na to, by class es było kowariantne i sprzeczne. Nie wiem, jak trudne, ale to brzmi jak lepsze wykorzystanie czasu dla mnie.

Z drugiej strony ktoś wspomniał, że byłoby bardzo fajnie mieć prawdziwą funkcję podobną do yield return dostępną w metodzie async. Mam na myśli, bez sztuczek z ręki.

+5

'async',' await' polega na istnieniu odpowiedniej metody 'GetAwaiter', która jest już oddzielona od klasy' Task'. – Lee

+0

Stoję edytowane/poprawione –

6

Zdaję sobie sprawę, że jestem późno do partii, ale tutaj jest metoda rozszerzenie używam w celu uwzględnienia tej brakującej funkcji:

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

ten sposób można po prostu zrobić:

class ResultBase {} 
class Result : ResultBase {} 

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