Eu quero escrever um método assíncrono com um out
parâmetro, como este:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
Como faço isso GetDataTaskAsync
?
fonte
Eu quero escrever um método assíncrono com um out
parâmetro, como este:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
Como faço isso GetDataTaskAsync
?
Você não pode ter métodos assíncronos com ref
ou out
parâmetros.
Lucian Wischik explica por que isso não é possível neste segmento do MSDN: http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have -ref-ou-out-parâmetros
Por que os métodos assíncronos não oferecem suporte a parâmetros por referência? (ou parâmetros de referência?) Essa é uma limitação do CLR. Optamos por implementar métodos assíncronos de maneira semelhante aos métodos do iterador - ou seja, através do compilador transformando o método em um objeto de máquina de estado. O CLR não possui uma maneira segura de armazenar o endereço de um "parâmetro de saída" ou "parâmetro de referência" como um campo de um objeto. A única maneira de oferecer suporte a parâmetros fora de referência seria se o recurso assíncrono fosse executado por uma reescrita de baixo nível do CLR em vez de uma reescrita do compilador. Examinamos essa abordagem e ela tinha muito a oferecer, mas acabaria sendo tão caro que nunca teria acontecido.
Uma solução típica para essa situação é fazer com que o método assíncrono retorne uma Tupla. Você pode reescrever seu método como tal:
public async Task Method1()
{
var tuple = await GetDataTaskAsync();
int op = tuple.Item1;
int result = tuple.Item2;
}
public async Task<Tuple<int, int>> GetDataTaskAsync()
{
//...
return new Tuple<int, int>(1, 2);
}
Tuple
alternativa. Muito útil.Tuple
. : PVocê não pode ter
ref
ouout
parâmetros nosasync
métodos (como já foi observado).Isso exige alguma modelagem nos dados que se deslocam:
Você ganha a capacidade de reutilizar seu código com mais facilidade, além de ser muito mais legível do que variáveis ou tuplas.
fonte
A solução C # 7 + é usar sintaxe implícita de tupla.
O resultado de retorno utiliza os nomes de propriedades definidas pela assinatura do método. por exemplo:
fonte
Alex fez um grande ponto na legibilidade. Da mesma forma, uma função também é interface suficiente para definir o (s) tipo (s) que está sendo retornado e você também obtém nomes significativos de variáveis.
Os chamadores fornecem um lambda (ou uma função nomeada) e o intellisense ajuda copiando o (s) nome (s) da variável do delegado.
Essa abordagem específica é como um método "Try", onde
myOp
é definido se o resultado do método fortrue
. Caso contrário, você não se importamyOp
.fonte
Uma característica interessante dos
out
parâmetros é que eles podem ser usados para retornar dados, mesmo quando uma função lança uma exceção. Eu acho que o equivalente mais próximo de fazer isso com umasync
método seria usar um novo objeto para armazenar os dados aos quais oasync
método e o chamador podem se referir. Outra maneira seria passar um delegado como sugerido em outra resposta .Observe que nenhuma dessas técnicas terá o tipo de imposição do compilador que
out
possui. Ou seja, o compilador não exigirá que você defina o valor no objeto compartilhado ou chame um delegado passado.Aqui está um exemplo de implementação usando um objeto compartilhado para imitar
ref
eout
para uso comasync
métodos e outros cenários onderef
eout
não estão disponíveis:fonte
Eu amo o
Try
padrão. É um padrão arrumado.Mas, é um desafio
async
. Isso não significa que não temos opções reais. Aqui estão as três principais abordagens que você pode considerar para osasync
métodos em uma quase versão doTry
padrão.Abordagem 1 - produzir uma estrutura
Isso se parece mais com um
Try
método de sincronização , retornando apenas um emtuple
vez de abool
com umout
parâmetro, que todos sabemos que não é permitido em C #.Com um método que retorna
true
defalse
e nunca lança umaexception
.Abordagem 2 - aprovação de métodos de retorno de chamada
Podemos usar
anonymous
métodos para definir variáveis externas. É uma sintaxe inteligente, embora um pouco complicada. Em pequenas doses, tudo bem.O método obedece ao básico do
Try
padrão, mas define osout
parâmetros para serem passados nos métodos de retorno de chamada. É feito assim.Abordagem 3 - use ContinueWith
E se você apenas usar o
TPL
como projetado? Sem tuplas. A idéia aqui é que usamos exceções para redirecionarContinueWith
para dois caminhos diferentes.Com um método que lança
exception
quando existe algum tipo de falha. Isso é diferente de retornar aboolean
. É uma maneira de se comunicar com oTPL
.No código acima, se o arquivo não for encontrado, uma exceção será lançada. Isso invocará a falha
ContinueWith
que será manipuladaTask.Exception
em seu bloco lógico. Legal, não é?Boa sorte.
fonte
ContinueWith
chamadas em cadeia têm o resultado esperado? Segundo meu entendimento, o segundoContinueWith
verificará o sucesso da primeira continuação, não o sucesso da tarefa original.Eu tive o mesmo problema que eu gosto de usar o padrão Try-method, que basicamente parece ser incompatível com o paradigma assíncrono-aguarde ...
Importante para mim é que eu posso chamar o método Try dentro de uma única cláusula if e não preciso pré-definir as variáveis out antes, mas posso fazê-lo em linha, como no exemplo a seguir:
Então, eu vim com a seguinte solução:
Defina uma estrutura auxiliar:
Defina o método Try assíncrono como este:
Chame o método Try assíncrono como este:
Para vários parâmetros de saída, você pode definir estruturas adicionais (por exemplo, AsyncOut <T, OUT1, OUT2>) ou pode retornar uma tupla.
fonte
A limitação dos
async
métodos que não aceitamout
parâmetros se aplica apenas aos métodos assíncronos gerados pelo compilador, declarados com aasync
palavra - chave. Não se aplica a métodos assíncronos criados à mão. Em outras palavras, é possível criarTask
métodos de retorno que aceitamout
parâmetros. Por exemplo, digamos que já temos umParseIntAsync
método que lança, e queremos criar umTryParseIntAsync
que não seja lançado. Nós poderíamos implementá-lo assim:Usando o
TaskCompletionSource
e oContinueWith
método é um pouco estranho, mas não há outra opção, uma vez que não podemos usar a convenienteawait
palavra-chave dentro deste método.Exemplo de uso:
Atualização: se a lógica assíncrona for muito complexa para ser expressa sem
await
, ela poderá ser encapsulada dentro de um delegado anônimo assíncrono aninhado. ATaskCompletionSource
ainda seria necessário para oout
parâmetro. É possível que oout
parâmetro possa ser concluído antes da conclusão da tarefa principal, como no exemplo abaixo:Este exemplo assume a existência de três métodos assíncronos
GetResponseAsync
,GetRawDataAsync
eFilterDataAsync
que são chamados em sucessão. Oout
parâmetro é concluído na conclusão do segundo método. OGetDataAsync
método pode ser usado assim:Aguardar o
data
antes de aguardarrawDataLength
é importante neste exemplo simplificado, porque, no caso de uma exceção, oout
parâmetro nunca será concluído.fonte
Eu acho que usar ValueTuples assim pode funcionar. Você deve adicionar primeiro o pacote ValueTuple NuGet:
fonte
Aqui está o código da resposta do @ dcastro modificado para C # 7.0 com tuplas nomeadas e desconstrução de tuplas, que simplifica a notação:
Para obter detalhes sobre as novas tuplas nomeadas, literais de tupla e desconstruções de tupla, consulte: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/
fonte
Você pode fazer isso usando a TPL (biblioteca paralela de tarefas) em vez de usar diretamente a palavra-chave wait.
fonte