Parâmetro padrão para CancelamentoToken

96

Tenho um código assíncrono ao qual gostaria de adicionar um CancellationToken. No entanto, existem muitas implementações onde isso não é necessário, então eu gostaria de ter um parâmetro padrão - talvezCancellationToken.None . Contudo,

Task<x> DoStuff(...., CancellationToken ct = null)

rendimentos

Um valor do tipo '' não pode ser usado como parâmetro padrão porque não há conversões padrão para o tipo 'System.Threading.CancellationToken'

e

Task<x> DoStuff(...., CancellationToken ct = CancellationToken.None)

O valor do parâmetro padrão para 'ct' deve ser uma constante de tempo de compilação

Existe alguma maneira de ter um valor padrão para CancellationToken?

tofutim
fonte

Respostas:

151

Acontece que o seguinte funciona:

Task<x> DoStuff(...., CancellationToken ct = default(CancellationToken))

que, de acordo com a documentação , é interpretado da mesma forma que CancellationToken.None:

Você também pode usar a default(CancellationToken)instrução C # para criar um token de cancelamento vazio.

tofutim
fonte
4
Isso é exatamente o que o framework atualmente faz internamente, mas eu não faria no meu código. Pense no que acontecerá com seu código se a Microsoft mudar sua implementação e CancellationToken.Nonese tornar algo mais default(CancellationToken).
noseratio
10
@Noseratio Isso quebraria a compatibilidade com versões anteriores em grande forma, então eu não esperava que isso acontecesse. E o que mais faria default(CancellationToken)?
svick
3
@Noseratio: Você está sendo muito rígido. As chances são de que CancellationToken.Nonese tornará de fato obsoleto Até a Microsoft está usando em seu default(CancellationToken)lugar. Por exemplo, consulte os resultados da pesquisa no código-fonte do Entity Framework.
drowa
21
Da propriedade MSDN Cancelamento.None : "Você também pode usar a instrução C # padrão (CancelamentoToken) para criar um token de cancelamento vazio" . Nenhum é um erro até que uma versão futura do C # o aceite como parâmetro padrão.
MuiBienCarlota
5
O mesmo aqui Para compensar as duas combinações intermediárias ausentes, os desenvolvedores podem passar Nenhum ou um Cancelamento padrão para o parâmetro cancellationToken e nulo para o parâmetro progresso.
Arek Bal
25

Existe alguma maneira de ter um valor padrão para CancelamentoToken?

Infelizmente, isso não é possível, pois CancellationToken.Nonenão é uma constante de tempo de compilação, que é um requisito para valores padrão em argumentos opcionais.

Você pode fornecer o mesmo efeito, no entanto, criando um método sobrecarregado em vez de tentar usar os parâmetros padrão:

Task<x> DoStuff(...., CancellationToken ct)
{
    //...
}

Task<x> DoStuff(....)
{
    return DoStuff(...., CancellationToken.None);
}
Reed Copsey
fonte
6
Essa é a maneira recomendada de lidar com isso, conforme explicado na documentação do padrão assíncrono baseado em tarefas no MSDN (especificamente na seção Escolhendo as sobrecargas a serem fornecidas ).
Sam Harwell
CancelamentoToken.None == default true
eoleary
5
Qual é o problema CancellationToken cancellationToken = default(CancellationToken)? Também descrito aqui blogs.msdn.microsoft.com/andrewarnottms/2014/03/19/…
Ray
20

Aqui estão várias soluções, em ordem decrescente de bondade geral:

1. Usando default(CancellationToken)como valor padrão:

Task DoAsync(CancellationToken ct = default(CancellationToken)) {  }

Semanticamente, CancellationToken.Noneseria o candidato ideal para o padrão, mas não pode ser usado como tal porque não é uma constante de tempo de compilação. default(CancellationToken)é a próxima melhor coisa porque é uma constante de tempo de compilação e oficialmente documentada para ser equivalente aCancellationToken.None .

2. Fornecer uma sobrecarga de método sem um CancellationTokenparâmetro:

Ou, se você preferir sobrecargas de método em vez de parâmetros opcionais (consulte esta e esta pergunta sobre esse tópico):

Task DoAsync(CancellationToken ct) {  } // actual method always requires a token
Task DoAsync() => DoAsync(CancellationToken.None); // overload producing a default token

Para métodos de interface, o mesmo pode ser alcançado usando métodos de extensão:

interface IFoo
{
    Task DoAsync(CancellationToken ct);
}

static class Foo
{
    public static Task DoAsync(this IFoo foo) => foo.DoAsync(CancellationToken.None);
}

Isso resulta em uma interface mais estreita e evita que os implementadores gravem explicitamente a sobrecarga do método de encaminhamento.

3. Tornando o parâmetro anulável e usando nullcomo valor padrão:

Task DoAsync(…, CancellationToken? ct = null)
{
     ct ?? CancellationToken.None 
}

Gosto menos dessa solução porque os tipos anuláveis ​​vêm com uma pequena sobrecarga de tempo de execução e as referências ao token de cancelamento se tornam mais detalhadas por causa do operador de coalescência nulo ??.

stakx - não contribui mais
fonte
11

Outra opção é usar um Nullable<CancellationToken>parâmetro, padronizá-lo nulle lidar com ele dentro do método:

Task<x> DoStuff(...., CancellationToken? ct = null) {
    var token = ct ?? CancellationToken.None;
    ...
}
Todd Menier
fonte
brilhante! de longe esta é a melhor resposta
John Henckel
4

As versões mais recentes do C # permitem uma sintaxe simplificada para a versão padrão (CancelamentoToken). Por exemplo:

Task<x> DoStuff(...., CancellationToken ct = default)
Alexei
fonte
-1

A resposta de Tofutim é uma maneira, mas pelos comentários vejo que as pessoas têm problemas com isso.

Nesse caso, sugeri que se pode ter um método como segue:

Task<x> DoStuff(...., CancellationToken ct)
{
} 

e sobrecarregá-lo como:

Task<x> DoStuff(....)
{
    return DoStuff(...., CancellationToken.None);
}

Isso compila, porque o valor de CancellationToken.Nonenão é necessário no momento da compilação.

Ozair Kafray
fonte