2016-06-14 22 views
5

Esistono molti metodi comuni bool TryXXX(out T result) in .NET BCL, l'essere più popolare, probabilmente, int.TryParse(...).async TryParse (...) modello

Vorrei implementare un metodo asyncTryXXX(). Ovviamente, non posso usare i parametri out.

Esiste uno schema stabilito per questo?

Più precisamente, ho bisogno di scaricare e analizzare un file. È possibile che il file non esista.

Questo è quello che mi è venuto in mente finora:

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

Fino a quando non viene implementata questa sintassi https://github.com/dotnet/roslyn/issues/347, sembra che si sia condannati a restituire il proprio tipo.Almeno utilizzare Tuple (o un tipo personalizzato migliore con nomi di proprietà ragionevoli come Success e Result), non KeyValuePair. – Evk

+1

Thx per il link, bella proposta! Qual è il vantaggio di Tuple <> su KVP <>? Ho sempre bisogno di restituire esattamente due elementi, il risultato vero/falso e il carico utile effettivo. Odio i nomi delle proprietà di 'Tuple <>' ('.Item1',' .Item2' ... completamente opachi a ciò che contiene la tupla.). 'KVP' non è molto meglio (nel mio scenario), ma almeno' .Value' rende * un po '* senso. –

+0

La differenza è solo semantica. KeyValuePair è, beh, chiave + valore. Nel tuo caso, niente è davvero importante. La tupla ha solo due (o più) valori arbitrari - il tuo caso. Se non ti piacciono i nomi, crea la tua classe TryAsyncResult con le proprietà bool e T con i nomi appropriati. Hai solo bisogno di una classe di questo tipo per tutte le tue operazioni Try (a causa di generici). – Evk

risposta

3

mi è venuta in mente le seguenti definizioni. I parametri defaultValue sono principalmente in grado di sovraccaricare il metodo TryGet, poiché i vincoli generici non fanno parte della firma di un metodo, ciò che rende il metodo univoco quando si decide quale metodo chiamare (ad esempio, anche il tipo restituito non fa parte di la firma).

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

È necessario esaminare la gestione delle eccezioni, questo esempio gestisce le eccezioni di analisi comuni. Potrebbe essere più sensato reagire ad altre eccezioni, come InvalidOperationException e NotSupportedException, probabilmente i tipi di eccezioni più utilizzati sul framework stesso (non necessariamente quelli generati più comunemente).

Un altro approccio consiste nel rilanciare eccezioni critiche, ad esempio ThreadAbortException e avere una clausola catch-all semplice che restituisce il valore predefinito. Tuttavia, questo nasconderà ogni eccezione non ritenuta critica, non importa quanto sia grave.

Come tale, e poiché il lancio di eccezioni è un'operazione costosa, è Parse che viene solitamente definito in termini di TryParse. Quindi il tuo TryGet dovrebbe avere un contratto, ad es. trattare con OperationCanceledException, che include TaskCanceledException e nient'altro.

Infine, è necessario denominarlo TryGetAsync, seguendo la convenzione del suffisso Asincrono. [1][2]

1

Una delle possibili decisioni è un array di ParsedFile, contenente 0 o 1 elemento.

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

Ora è possibile controllare il risultato:

. . . 

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

. . . 

Se si desidera chiamare void metodo, è possibile utilizzare ?. operatore:

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

UPDATE

In Azure puoi usare ConditionalValue<TValue> class.

+0

ConditionalValue sembra interessante. Quella pagina menziona "API di collezioni affidabili" ma nessun collegamento, e manca in dettaglio ... –

+0

PS Trovato un collegamento migliore per "Raccolte attendibili" sembra essere un'offerta di servizi di Azure Service Fabric. https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-reliable-collections –