c# - subtitulada - ¿Por qué el compilador se queja de que ''no todas las rutas de código devuelven un valor'' cuando puedo ver claramente que lo hacen?
i can see clearly now subtitulada español e ingles (3)
Estoy tratando de averiguar por qué el compilador tiene un problema con esta función. Me da el error "No todas las rutas de código devuelven un valor", sin embargo, no puedo ver una situación en la que el flujo de control pase a la expresión if( a )
sin ser verdadero (por lo que if( a )
es superfluo, pero el compilador no parece reconocer eso).
public static Boolean Foo(Boolean x)
{
Boolean a = false;
if( x )
{
a = true;
}
else
{
try
{
SomethingThatMightThrow();
Assert.IsFalse( a );
return a;
}
catch(Exception)
{
a = true;
}
}
if( a )
{
return x;
}
}
La solución inmediata es simplemente eliminar la instrucción if( a )
guard y return x
solo return x
inmediatamente, pero ¿por qué el compilador se queja aunque debería poder probar de forma estática que todas las rutas de código posibles alcanzarán una declaración de return
? Fundamentalmente, no hay bucles, que a menudo son la razón principal por la que no se puede probar el return
.
Estoy usando VS2015 Update 3.
Creo que el compilador hace un análisis muy simple en el código y, por lo tanto, el retorno debe darse explícitamente.
Esto puede parecer una decisión equivocada, pero cuando se trata de código complejo, el valor devuelto puede no ser claro. Entonces, el programador se ve obligado a devolverlo.
Su ejemplo se puede reducir a un mínimo como este:
public static Int32 Main(String[] args)
{
var printUsage = true;
if (printUsage)
{
return 0;
}
// return nothing, so compiler is not happy
}
mientras sigue recibiendo el error.
NOTA: si usa Resharper, realizará el análisis que desee y le avisará en consecuencia:
if (printUsage) // Warning: expression is always true
Existe un escenario admitido donde a
es false
cuando llega al final de su función. Ese escenario es cuando estás depurando tu código y usas tu depurador para establecer a
false
.
Las reglas del compilador de C # son simples en diseño. En los lenguajes que C # toma prestados, el problema de las funciones que potencialmente no devuelven nada fue un problema que no pudo ser cubierto por las advertencias del compilador. Había demasiados falsos positivos, por lo tanto, las advertencias se modificaron para advertir sobre los casos obvios, introduciendo falsos negativos. Las reglas de C # son un compromiso donde los falsos positivos son aceptables si son comprensibles para un humano que está familiarizado con las reglas, y los falsos negativos son inaceptables. Se le garantiza que si su función tiene una ruta de código que no devuelve un valor, el compilador lo detecta.
Una parte de esas reglas simples es que los valores de las variables no se consideran. Incluso si está garantizado estáticamente que una true
, el compilador por diseño no puede hacer uso de ese hecho.
@PetSerAl ya citó la redacción relevante en la especificación del lenguaje C #:
8.1 Puntos finales y accesibilidad.
[...]
Para determinar si se puede alcanzar una declaración particular o un punto final, el compilador realiza un análisis de flujo de acuerdo con las reglas de accesibilidad definidas para cada declaración. El análisis de flujo toma en cuenta los valores de las expresiones constantes (§7.19) que controlan el comportamiento de las declaraciones, pero no se consideran los valores posibles de las expresiones no constantes. En otras palabras, para propósitos de análisis de flujo de control, se considera que una expresión no constante de un tipo dado tiene cualquier valor posible de ese tipo.
Esto es parte de la última especificación de idioma publicada actualmente, la de C # 5.0. La versión que está utilizando, C # 6.0 (eso es lo que ofrece VS2015), aún no tiene una especificación publicada, por lo que es posible que la redacción sea un poco diferente, pero como ha demostrado su compilador, efectivamente se aplica la misma regla.
Es tiempo de ejecución vs. tiempo de compilación
Tu ejemplo es demasiado complicado. Esto no compilará tampoco:
static int Test()
{
bool f = true;
if (f)
{
return 1;
}
else
{
//Not all code paths return a value
}
}
Por otro lado, esto va a:
static int Test()
{
if (true)
{
return 1;
}
else
{
//No error
}
}
Supongo que, independientemente de los mecanismos de validación existentes, no hay lógica suficiente para inferir el contenido de una variable en tiempo de ejecución. Las variables de tiempo de compilación no son un problema.