Después de haber migrado mi proyecto de VS2013 a VS2015, el proyecto ya no se compila. Se produce un error de compilación en la siguiente declaración LINQ:
static void Main(string[] args)
{
    decimal a, b;
    IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
    var result = (from v in array
                  where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
                  orderby decimal.Parse(v)
                  select v).ToArray();
}
El compilador devuelve un error:
Error CS0165 Uso de variable local no asignada 'b'
¿Qué causa este problema? ¿Es posible solucionarlo a través de una configuración de compilador?

bdespués de asignarlo mediante unoutparámetro.outargumentos. ¿TryParseEso devolvería un valor anulable (o equivalente)?where (a = decimal.TryParse(v)).HasValue && (b = decimal.TryParse(v)).HasValue && a <= bve mucho mejordecimal a, b; var q = decimal.TryParse((dynamic)"10", out a) && decimal.TryParse("15", out b) && a <= b;. He abierto un error Roslyn levantar esto.Respuestas:
Me parece un error del compilador. Al menos, lo hizo. Aunque las expresiones
decimal.TryParse(v, out a)ydecimal.TryParse(v, out b)se evalúan dinámicamente, esperaba que el compilador aún entendiera que para cuando lleguea <= b, ambosaybdefinitivamente están asignados. Incluso con las rarezas que se pueden encontrar en la escritura dinámica, esperaría evaluar soloa <= bdespués de evaluar ambasTryParsellamadas.Sin embargo, resulta que a través del operador y la conversión engañosa, es completamente factible tener una expresión
A && B && Cque evalúeAyCpero noB, si es lo suficientemente astuto. Consulte el informe de errores de Roslyn para ver el ingenioso ejemplo de Neal Gafter.Hacer que funcione
dynamices aún más difícil: la semántica involucrada cuando los operandos son dinámicos es más difícil de describir, porque para realizar una resolución de sobrecarga, es necesario evaluar los operandos para averiguar qué tipos están involucrados, lo que puede ser contrario a la intuición. Sin embargo, de nuevo Neal ha llegado con un ejemplo que demuestra que se requiere que el error del compilador ... esto no es un error, es un error del arreglo . Enormes felicitaciones a Neal por demostrarlo.No, pero existen alternativas que evitan el error.
En primer lugar, puede evitar que sea dinámico: si sabe que solo usará cadenas, entonces podría usar
IEnumerable<string>o darle a la variable de rangovun tipo destring(es decirfrom string v in array). Esa sería mi opción preferida.Si realmente necesita mantenerlo dinámico, simplemente dé
bun valor para comenzar con:Esto no hará ningún daño: sabemos que en realidad su evaluación dinámica no hará nada loco, por lo que aún terminará asignando un valor a
bantes de usarlo, haciendo que el valor inicial sea irrelevante.Además, parece que agregar paréntesis también funciona:
Eso cambia el punto en el que se activan varias piezas de resolución de sobrecarga, y hace feliz al compilador.
Hay una cuestión que aún permanecen - reglas de la especificación de la asignación definitiva a la
&&necesidad del operador a aclararse a estado que sólo se aplican cuando el&&operador se está utilizando en su aplicación "regular" con dosbooloperandos. Intentaré asegurarme de que esto se solucione para el próximo estándar ECMA.fuente
IEnumerable<string>o agregar corchetes funcionó para mí. Ahora el compilador se compila sin errores.decimal a, b = 0m;podría eliminar el error, pero luegoa <= busaría siempre0m, ya que el valor de salida aún no se ha calculado.decimal? TryParseDecimal(string txt)puede ser una solución tambiénbpodría no asignarse"; Sé que es un razonamiento inválido pero explica por qué el paréntesis lo corrige ...Esto parece ser un error, o al menos una regresión, en el compilador de Roslyn. Se ha presentado el siguiente error para rastrearlo:
Mientras tanto, la excelente respuesta de Jon tiene un par de soluciones.
fuente
Como me educaron tan duro en el informe de errores, intentaré explicarlo yo mismo.
Imagine
Tes un tipo definido por el usuario con una conversión implícitaboolque alterna entrefalseytrue, comenzando confalse. Por lo que el compilador sabe, eldynamicprimer argumento del primero&&podría evaluar ese tipo, por lo que tiene que ser pesimista.Si, entonces, deja que el código se compile, esto podría suceder:
&&, hace lo siguiente:T- implícitamente lo envía abool.false, así que no necesitamos evaluar el segundo argumento.&&evaluación sea el primer argumento. (No, nofalse, por alguna razón).&&, hace lo siguiente:T- implícitamente lanzarlo abool.true, así que evalúe el segundo argumento.bno está asignado.En términos de especificaciones, en resumen, existen reglas especiales de "asignación definida" que nos permiten decir no solo si una variable está "definitivamente asignada" o "no definitivamente asignada", sino también si está "definitivamente asignada después de la
falseinstrucción" o "definitivamente asignado despuéstruedeclaración ".Estos existen para que al tratar con
&&y||(y!y??y?:) el compilador pueda examinar si se pueden asignar variables en ramas particulares de una expresión booleana compleja.Sin embargo, estos solo funcionan mientras los tipos de expresiones permanecen booleanos . Cuando parte de la expresión es
dynamic(o un tipo estático no booleano) ya no podemos decir de manera confiable que la expresión estrueofalse; la próxima vez que la usemosboolpara decidir qué rama tomar, es posible que haya cambiado de opinión.Actualización: esto ahora se ha resuelto y documentado :
fuente
outa un método que tengaref. Felizmente lo hará, y hará asignadas variables, sin cambiar el valor.false/trueen lugar del operador de conversión implícito? Localmente, llamaráimplicit operator boolal primer argumento, luego invocará el segundo operando, llamaráoperator falseal primer operando, seguido de nuevoimplicit operator boolal primer operando . Esto no tiene sentido para mí, el primer operando debería esencialmente reducirse a un booleano una vez, ¿no?dynamicencadenado&&? Lo he visto básicamente ir (1) evaluar el primer argumento (2) usar la conversión implícita para ver si puedo cortocircuitar (3) No puedo, así que evalúe el segundo argumento (4) ahora conozco ambos tipos, yo Puedo ver que lo mejor&&es un&operador de llamada definido por el usuario (5)falseen el primer argumento para ver si puedo cortocircuitar (6) Puedo (porquefalseyimplicit boolno estoy de acuerdo), por lo que el resultado es el primer argumento ... y luego el siguiente&&, (7) uso de conversión implícita para ver si puedo cortocircuitar (de nuevo).Esto no es un error. Consulte https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713 para ver un ejemplo de cómo una expresión dinámica de este formulario puede dejar una variable sin asignar.
fuente