Sto modificando una libreria per aggiungere metodi asincroni. Da Should I expose synchronous wrappers for asynchronous methods? si afferma che non dovrei semplicemente scrivere un wrapper attorno a Task.Result
quando si chiama il metodo sincrono. Ma come faccio a non dover duplicare un sacco di codice tra metodi asincroni e metodi di sincronizzazione, dato che vogliamo mantenere entrambe le opzioni nella libreria?Pattern per scrivere i metodi sincroni e asincroni nelle librerie e tenerlo DRY
Ad esempio, la libreria utilizza attualmente il metodo TextReader.Read
. Parte del cambiamento asincrono vorremmo usare il metodo TextReader.ReadAsync
. Poiché questo è al centro delle librerie, sembrerebbe che avrei bisogno di duplicare un sacco di codice tra i metodi sincroni e asincroni (voglio mantenere il codice DRY il più possibile). O ho bisogno di rifattorizzarli in un PreRead
e PostRead
metodi che sembrano ingombrare il codice e ciò che il TPL stava cercando di risolvere.
Sto pensando di avvolgere il metodo TextReader.Read
in un Task.Return()
. Anche se si tratta di un compito, i miglioramenti del TPL non dovrebbero farlo passare a un thread diverso e posso ancora usare async per attendere la maggior parte del codice come normale. Sarebbe quindi ok se un wrapper del sincrono fosse solo Task.Result
o Wait()
?
Ho esaminato altri esempi nella libreria .net. Lo StreamReader
sembra duplicare il codice tra asincrono e non asincrono. Il MemoryStream
fa un Task.FromResult
.
Anche pianificando ovunque potrei aggiungere ConfigureAwait(false)
in quanto è solo una libreria.
Aggiornamento:
di cosa sto parlando di codice duplicato è
public decimal ReadDecimal()
{
do
{
if (!Read())
{
SetInternalProperies()
}
else
{
return _reader.AsDecimal();
}
} while (_reader.hasValue)
}
public async Task<decimal> ReadDecimalAsync()
{
do
{
if (!await ReadAsync())
{
SetInternalProperies()
}
else
{
return _reader.AsDecimal();
}
} while (_reader.hasValue)
}
Questo è un piccolo esempio, ma si può vedere l'unico cambiamento di codice è la attesa e il compito.
Per essere chiari, desidero codificare utilizzando async/await e TPL ovunque nella libreria, ma ho ancora bisogno di avere anche i vecchi metodi di sincronizzazione. Non sto solo per il Task.FromResult()
metodi di sincronizzazione. Quello che stavo pensando stava avendo una bandiera che dice io voglio il metodo di sincronizzazione e alla radice controllare il flag qualcosa come
public decimal ReadDecimal()
{
return ReadDecimalAsyncInternal(true).Result;
}
public async Task<decimal> ReadDecimal()
{
return await ReadDecimalAsyncInternal(false);
}
private async Task<decimal> ReadDecimalAsyncInternal(bool syncRequest)
{
do
{
if (!await ReadAsync(syncRequest))
{
SetInternalProperies()
}
else
{
return _reader.AsDecimal();
}
} while (_reader.hasValue)
}
private Task<bool> ReadAsync(bool syncRequest)
{
if(syncRequest)
{
return Task.FromResult(streamReader.Read())
}
else
{
return StreamReader.ReadAsync();
}
}
Perché si desidera esporre la versione sincrona e asincrona del metodo? Un'operazione può essere sincrona o asincrona. Non può essere entrambi. Scegli la cosa giusta, non entrambe. Quindi con il codice cliente, puoi chiamarlo comunque. –
È una biblioteca. Abbiamo codice esistente che lo usa e non vogliamo dover cambiare tutto questo. È la stessa cosa di TextReader.Read e TextReader.ReadAsync stiamo aggiungendo il metodo "asincrono" alla libreria come Library.Method e Library.MethodAsync – CharlesNRice
Un wrapper Task.Result non fornisce nulla di positivo. attendere attiverà semplicemente in modo sincrono il valore memorizzato in quell'attività. La firma del metodo è fuorviante. Esponi solo i metodi asincroni che sono in realtà asincroni internamente. – usr