2016-06-14 27 views
5

Istnieje wiele typowych metod bool TryXXX(out T result) w .NET BCL, najpopularniejszej prawdopodobnie, int.TryParse(...).async TryParse (...) wzorzec

Chciałbym zastosować metodę asynchroniczną asyncTryXXX(). Oczywiście nie mogę używać parametrów out.

Czy istnieje ustalony wzór?

Co ważniejsze, muszę pobrać i przeanalizować plik. Możliwe, że plik nie istnieje.

To właśnie wpadł do tej pory:

public async Task<DownloadResult> TryDownloadAndParse(string fileUri) 
{ 
    try 
    { 
     result = await DownloadAndParse(fileUri); //defined elsewhere 
     return new DownloadResult {IsFound = true, Value = result} 
    } 
    catch (DownloadNotFoundException ex) 
    { 
     return new DownloadResult {IsFound = false, Value = null} 
    } 
    //let any other exception pass 
} 

public struct DownloadResult 
{ 
    public bool IsFound { get; set; } 

    public ParsedFile Value { get; set; } 
} 
+1

Dopóki ta składnia https://github.com/dotnet/roslyn/issues/347 nie zostanie zaimplementowana, wydaje się, że jesteś skazany na to, by po prostu zwrócić swój własny typ.Przynajmniej używaj Tuple (lub lepszego niestandardowego typu z rozsądnymi nazwami właściwości, takimi jak Sukces i Wynik), a nie KeyValuePair. – Evk

+1

Thx za link, fajna propozycja! Jaka jest korzyść z Tuple <> ponad KVP <>? Zawsze muszę zwrócić dokładnie dwie rzeczy, wynik prawdziwy/fałszywy i rzeczywistą ładowność. Nienawidzę nazw właściwości 'Tuple <>' ('.Item1',' .Item2' ... całkowicie nieprzezroczystych do tego, co trzyma krotka.). 'KVP' nie jest dużo lepszy (w moim scenariuszu), ale przynajmniej' .Value' sprawia, że ​​* jakiś * sens. –

+0

Różnica jest tylko semantyką. KeyValuePair to, cóż, klucz + wartość. W twoim przypadku nic naprawdę nie jest kluczowe. Tuple to tylko dwie (lub więcej) dowolne wartości - Twoja sprawa. Jeśli nie lubisz nazw, utwórz własną klasę TryAsyncResult z właściwościami bool i T z odpowiednimi nazwami. Potrzebujesz tylko jednej takiej klasy dla wszystkich twoich operacji Try (z powodu generycznych). – Evk

Odpowiedz

3

Doszedłem się z następującymi definicjami. Parametry defaultValue są głównie w stanie przeciążać metodę TryGet, ponieważ ograniczenia ogólne nie są częścią sygnatury metody, co sprawia, że ​​metoda jest unikalna przy podejmowaniu decyzji, która metoda wywołać (na przykład, typ powrotu nie jest również częścią podpis).

public async Task<T> TryGet<T>(Func<Task<T>> func, T defaultValue = null) where T : class 
{ 
    try 
    { 
     return await func(); 
    } 
    catch (ArgumentException) 
    { 
     return defaultValue; 
    } 
    catch (FormatException) 
    { 
     return defaultValue; 
    } 
    catch (OverflowException) 
    { 
     return defaultValue; 
    } 
} 

public async Task<Nullable<T>> TryGet<T>(Func<Task<T>> func, Nullable<T> defaultValue = null) where T : struct 
{ 
    try 
    { 
     return await func(); 
    } 
    catch (ArgumentException) 
    { 
     return defaultValue; 
    } 
    catch (FormatException) 
    { 
     return defaultValue; 
    } 
    catch (OverflowException) 
    { 
     return defaultValue; 
    } 
} 

Należy przejrzeć obsługę wyjątków, przykład ten obsługuje wspólne wyjątki analizowania. Może być bardziej sensowne reagowanie na inne wyjątki, takie jak InvalidOperationException i NotSupportedException, prawdopodobnie najczęściej używane typy wyjątków w samej strukturze (niekoniecznie najczęściej wyrzucane).

Innym podejściem jest ponowne rzucenie krytycznych wyjątków, takich jak ThreadAbortException i proste klauzula catch-all, która zwraca wartość domyślną. Jednak spowoduje to ukrycie każdego wyjątku, który nie zostanie uznany za krytyczny, niezależnie od tego, jak poważny jest.

Jako taki, a ponieważ wyjątki od rzucania są kosztowną operacją, jest to Parse, które jest zwykle definiowane jako TryParse. Twój TryGet powinien więc zawierać umowę, np. do czynienia z OperationCanceledException, która obejmuje TaskCanceledException i nic więcej.

Na koniec należy podać nazwę TryGetAsync, zgodnie z konwencją przyrostków Async. [1][2]

1

Jednym z możliwych decyzji jest tablicą ParsedFile, zawierający 0 lub 1 element.

public async Task<ParsedFile[]> TryDownloadAndParse(string fileUri) 
{ 
    try 
    { 
     return new[] { await DownloadAndParse(fileUri) }; 
    } 
    catch (DownloadNotFoundException ex) 
    { 
     return new ParsedFile[0]; 
    } 
} 

Teraz można sprawdzić wynik:

. . . 

var parsedFiles = await TryDownloadAndParse(url); 
if (parsedFiles.Any()) 
{ 
    var parsedFile = parsedFiles.Single(); 
    // more processing 
} 

. . . 

Jeśli chcesz zadzwonić void metody można użyć ?. operatora:

var parsedFiles = await TryDownloadAndParse(url); 
parsedFiles.SingleOrDefault()?.DoVeryImportantWorkWithoutResult(); 

UPDATE

W Azure można użyć ConditionalValue<TValue> class.

+0

Wartość warunkowa brzmi interesująco. Na tej stronie wymieniono "Niezawodne kolekcjonowania interfejsów API", ale nie ma linku i brakuje go w szczegółach ... –

+0

PS Znalazłem lepszy link do "wiarygodnych kolekcji", który wydaje się być usługową ofertą usługi Azure Service Fabric. https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-reliable-collections –