2012-08-30 10 views
11

Naprawdę lubię pracować z programowaniem asynchronicznym C# 5.0. Jest jednak kilka miejsc, w których aktualizacja starego kodu, który jest zgodny z modelem TAP, sprawia mi problemy.Może/powinien Zadanie <TResult> być zapakowane w C# 5.0, co jest kowariantne w TResult?

Oto jeden z nich - nie jestem pewien dokładnie dlaczego Task<TResult> nie kowariantna w TResult, ale to powoduje problemy dla mnie podczas próby aktualizacji kowariantna interfejs przenieść z synchronicznym do asychronous wzoru:

stary kod:

public interface IInitializable<out T> // ** out generic modifier ** 
{ 
    /// <summary> 
    /// Boolean to indicate if class is ready 
    /// </summary> 
    bool IsInitialized { get; } 

    /// <summary> 
    /// Calls for instance to be initialized using current parameters 
    /// Driver initialization can be done in the default constructor if desired 
    /// </summary> 
    T Initialize(); 
} 

Nowy kod (nie będzie skompilować):

public interface IAsyncInitializable<out T> // ** out generic modifier...broken ** 
{ 
    /// <summary> 
    /// Boolean to indicate if class is ready 
    /// </summary> 
    bool IsInitialized { get; } 

    /// <summary> 
    /// Calls for instance to be initialized using current parameters 
    /// Driver initialization can be done in the default constructor if desired 
    /// </summary> 
    Task<T> InitializeAsync(); // ** breaks because Task<TResult> is invariant in TResult ** 
} 

Czy istnieje rozsądny sposób obejść bez MO zbyt drastycznie ograniczasz moje interfejsy API? (Bonus: dlaczego Zadanie nie jest kowariantne?). Nie ma interfejsu IAwaitable, ale przypuszczam, że mógłbym go stworzyć i stworzyć metodę rozszerzenia, która przekształca się w owinięty, kowariantny, trwały obiekt zadania. Czy robię to źle?

+2

BTW, nawet jeśli 'Task' był kowariantna interfejs, Twój kod nie skompilować. Poprawna wersja to 'Task InitializeAsync();' (bez modyfikatora 'out' w tym wierszu). – svick

+0

Doskonały punkt. Poprawiłem to w VS, ale zapomniałem edytować SO. –

Odpowiedz

12

Task<T> nie może być kowariantny w T, ponieważ jest klasą. Tylko interfejsy i delegaci mogą mieć ogólną wariancję.

Co do tego, czy warto robić opakowanie ... Myślę, że to zależy od tego, ile wykorzystujesz kowariancji w swoim projekcie. Podejrzewam, że z czasem odkryjesz, że całe pakowanie i rozpakowywanie jest mylące - jeśli nie jest to złe, aby po prostu odnieść skutek usunięcia kowariancji, zrobiłbym to.

+0

Dzięki za poradę! Zaimplementowałem IAwaitable (out TResult) przy użyciu Eduasync TaskAwaiter (T) i widzę, że korzystanie z oddzielnego interfejsu zdecydowanie nie jest warte utraty czytelności/użyteczności. I, świetny punkt re: klasy i ogólna wariancja ... zaszczycony moim błędem wskazanym przez @ Jon Skeeta :). Jednak żaden program ITask (out T) nie jest złym wyborem. Widzę tutaj komentarz Stephena Cleary (http://stackoverflow.com/questions/8407227/async-generic-delegate-in-c-sharp-5-0), ale zgodność WinRT interop jest trochę niesatysfakcjonująca jako uzasadnienie dla użycia konkretny typ. –

+1

@DavidCuccia: Powinieneś przeczytać na blogu Stephena Touba o propagowaniu kontekstów, jeśli chodzi o osoby oczekujące. Robi się trochę trudniej niż w programach Eduasync :( –

+0

@JonSkeet - Każdy pomysł, dla którego twórcy framework'ów nie dodali 'ITask ' i zaimplementowali tylko klasę? –

4

Uważam, że nie uwzględnienie obsługi asynchronicznego słowa kluczowego async w interfejsie ITask było poważnym niedopatrzymaniem ze strony Microsoftu. Na szczęście nie jest zbyt trudno obejść to ograniczenie.

Zaimplementowałem interfejs, którego nie można zmienić na ITask<out TResult>. Jego użycie jest całkiem proste.

Więcej informacji można znaleźć na stronie:

https://github.com/jam40jeff/ITask

Powiązane problemy