Ambos geram um erro dizendo que devem ser uma constante em tempo de compilação:
void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))
Antes de tudo, alguém pode explicar por que esses valores não podem ser determinados em tempo de compilação? E existe uma maneira de especificar um valor padrão para um objeto TimeSpan opcional?
c#
c#-4.0
default-value
timespan
optional-parameters
Mike Pateras
fonte
fonte
new TimeSpan(2000)
isso não significa 2000 milissegundos, significa 2000 "ticks", que são 0,2 milissegundos ou um 10.000ésimo de dois segundos.Respostas:
Você pode contornar isso com muita facilidade alterando sua assinatura.
Devo elaborar - o motivo pelo qual essas expressões no seu exemplo não são constantes em tempo de compilação é porque, em tempo de compilação, o compilador não pode simplesmente executar TimeSpan.FromSeconds (2.0) e colar os bytes do resultado em seu código compilado.
Como exemplo, considere se você tentou usar DateTime.Now. O valor de DateTime.Now muda toda vez que é executado. Ou suponha que TimeSpan.FromSeconds levasse em conta a gravidade. É um exemplo absurdo, mas as regras das constantes em tempo de compilação não produzem casos especiais apenas porque sabemos que o TimeSpan.FromSeconds é determinístico.
fonte
<param>
, porque não está visível na assinatura.span = span ?? TimeSpan.FromSeconds(2.0);
com o tipo anulável, no corpo do método. Ouvar realSpan = span ?? TimeSpan.FromSeconds(2.0);
para obter uma variável local que não seja anulável.Minha herança VB6 me deixa desconfortável com a idéia de considerar "valor nulo" e "valor ausente" como equivalente. Na maioria dos casos, provavelmente é bom, mas você pode ter um efeito colateral não intencional ou pode engolir uma condição excepcional (por exemplo, se a origem de
span
for uma propriedade ou variável que não deve ser nula, mas é).Portanto, sobrecarregaria o método:
fonte
Isso funciona bem:
void Foo(TimeSpan span = default(TimeSpan))
fonte
TimeSpan
valores arbitrários , como o fornecido pornew TimeSpan(2000)
.O conjunto de valores que pode ser usado como valor padrão é o mesmo que pode ser usado para um argumento de atributo. A razão é que os valores padrão são codificados em metadados dentro do
DefaultParameterValueAttribute
.Por que não pode ser determinado em tempo de compilação. O conjunto de valores e expressões sobre esses valores permitidos no tempo de compilação está listado nas especificações oficiais da linguagem C # :
O tipo
TimeSpan
não se encaixa em nenhuma dessas listas e, portanto, não pode ser usado como uma constante.fonte
TimeSpan
pode caber o último nesta listadefault(TimeSpan)
é válido.fornecido
default(TimeSpan)
não é um valor válido para a função.Ou
fornecido
new TimeSpan()
não é um valor válido.Ou
Isso deve ser melhor, considerando que as chances de o
null
valor ser válido para a função são raras.fonte
TimeSpan
é um caso especial para oDefaultValueAttribute
e é especificado usando qualquer sequência que possa ser analisada por meio doTimeSpan.Parse
métodofonte
Minha sugestão:
BTW
TimeSpan.FromSeconds(2.0)
não é igualnew TimeSpan(2000)
- o construtor leva carrapatos.fonte
Outras respostas deram grandes explicações sobre por que um parâmetro opcional não pode ser uma expressão dinâmica. Mas, para recontar, os parâmetros padrão se comportam como constantes de tempo de compilação. Isso significa que o compilador deve ser capaz de avaliá-los e apresentar uma resposta. Existem pessoas que desejam que o C # adicione suporte ao compilador que avalia expressões dinâmicas ao encontrar declarações constantes - esse tipo de recurso estaria relacionado aos métodos de marcação como "puros", mas isso não é uma realidade agora e talvez nunca seja.
Uma alternativa para usar um parâmetro padrão C # para esse método seria usar o padrão exemplificado por
XmlReaderSettings
. Nesse padrão, defina uma classe com um construtor sem parâmetros e propriedades publicamente graváveis. Em seguida, substitua todas as opções pelos padrões do seu método por um objeto desse tipo. Mesmo torne esse objeto opcional, especificando um padrãonull
para ele. Por exemplo:Para chamar, use essa sintaxe estranha para instanciar e atribuir propriedades em uma única expressão:
Desvantagens
Essa é uma abordagem realmente pesada para resolver esse problema. Se você estiver escrevendo uma interface interna rápida e suja e tornando o valor
TimeSpan
nulo e o tratamento nulo como o valor padrão desejado funcionaria bem, faça isso.Além disso, se você tiver um grande número de parâmetros ou estiver chamando o método em um loop restrito, isso terá a sobrecarga das instanciações de classe. Obviamente, se chamar esse método em um loop apertado, pode ser natural e até muito fácil reutilizar uma instância do
FooSettings
objeto.Benefícios
Como mencionei no comentário no exemplo, acho que esse padrão é ótimo para APIs públicas. Adicionar novas propriedades a uma classe é uma alteração ABI ininterrupta, portanto, você pode adicionar novos parâmetros opcionais sem alterar a assinatura do seu método usando esse padrão - fornecendo mais opções ao código compilado mais recentemente, continuando a suportar o código compilado antigo sem trabalho extra .
Além disso, como os parâmetros de método padrão integrados do C # são tratados como constantes de compilação e inseridos no site de chamada, os parâmetros padrão serão usados apenas pelo código quando forem recompilados. Ao instanciar um objeto de configurações, o chamador carrega dinamicamente os valores padrão ao chamar seu método. Isso significa que você pode atualizar os padrões apenas alterando sua classe de configurações. Portanto, esse padrão permite alterar os valores padrão sem a necessidade de recompilar os chamadores para ver os novos valores, se desejado.
fonte