O código a seguir resulta no uso da variável local não atribuída "numberOfGroups" :
int numberOfGroups;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
numberOfGroups = 10;
}
No entanto, este código funciona bem (embora ReSharper diga que o = 10
é redundante):
int numberOfGroups = 10;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
numberOfGroups = 10;
}
Estou perdendo algo ou o compilador não está gostando do meu ||
?
Eu reduzi isso para dynamic
causar os problemas ( options
era uma variável dinâmica no meu código acima). A questão ainda permanece, por que não posso fazer isso ?
Este código não compila:
internal class Program
{
#region Static Methods
private static void Main(string[] args)
{
dynamic myString = args[0];
int myInt;
if(myString == null || !int.TryParse(myString, out myInt))
{
myInt = 10;
}
Console.WriteLine(myInt);
}
#endregion
}
No entanto, este código faz :
internal class Program
{
#region Static Methods
private static void Main(string[] args)
{
var myString = args[0]; // var would be string
int myInt;
if(myString == null || !int.TryParse(myString, out myInt))
{
myInt = 10;
}
Console.WriteLine(myInt);
}
#endregion
}
Eu não sabia que dynamic
isso seria um fator.
out
parâmetro como entradaout
parâmetro. Certamente é interessante considerar qual código auxiliar o compilador deve produzir para evitar o problema, ou se isso é possível.Respostas:
Tenho certeza de que é um bug do compilador. Belo achado!
Edit: não é um bug, como demonstra Quartermeister; dynamic pode implementar um
true
operador estranho que pode fazery
com que nunca seja inicializado.Aqui está uma reprodução mínima:
Não vejo razão para que isso seja ilegal; se você substituir dynamic por bool, ele compilará perfeitamente.
Na verdade, vou me encontrar com a equipe C # amanhã; Vou mencionar isso a eles. Desculpas pelo erro!
fonte
d
pode ser de um tipo com umtrue
operador sobrecarregado . Publiquei uma resposta com um exemplo em que nenhum dos ramos é usado.É possível que a variável não seja atribuída se o valor da expressão dinâmica for de um tipo com um operador sobrecarregado
true
.O
||
operador invocará otrue
operador para decidir se avalia o lado direito e, em seguida, aif
instrução invocará otrue
operador para decidir se avalia seu corpo. Para um normalbool
, eles sempre retornarão o mesmo resultado e então exatamente um será avaliado, mas para um operador definido pelo usuário não existe tal garantia!Construindo a partir da reprodução de Eric Lippert, aqui está um programa curto e completo que demonstra um caso em que nenhum caminho seria executado e a variável teria seu valor inicial:
fonte
d
ser avaliado duas vezes? (Não estou contestando que seja , como você mostrou.) Eu esperava que o resultado avaliado detrue
(da primeira chamada do operador, causa por||
) fosse "repassado" para aif
instrução. Isso certamente aconteceria se você colocasse uma chamada de função lá, por exemplo.d
é avaliada apenas uma vez, como você espera. É otrue
operador que está sendo chamado duas vezes, uma vez||
e outra vezif
.var cond = d || M(out y); if (cond) { ... }
. Primeiro avaliamosd
para obter umaEvilBool
referência de objeto. Para avaliar o||
, primeiro invocamosEvilBool.true
com essa referência. Que retorna verdadeiro, por isso curto-circuito e não invocarM
, e depois atribuir a referênciacond
. Em seguida, passamos para aif
declaração. Aif
instrução avalia sua condição chamandoEvilBool.true
.Do MSDN (ênfase minha):
Visto que o compilador não verifica o tipo ou resolve nenhuma operação que contenha expressões do tipo dinâmico, ele não pode garantir que a variável será atribuída por meio do uso de
TryParse()
.fonte
numberGroups
é atribuída (noif true
bloco), caso contrário, a segunda condição garante a atribuição (viaout
).myString == null
(contando apenas com oTryParse
).if
expressão) envolve umadynamic
variável, ela não é resolvida em tempo de compilação (o compilador, portanto, não pode fazer essas suposições).