2010-04-15 17 views
17

que tiene un código:código inalcanzable detectado en caso de declaración

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
    { 
     switch (keyData) 
     { 
      case Keys.Alt|Keys.D1: 

       if (this._condition1) 
       { 
        return true; 
       } 
       else 
       { 
        return base.ProcessCmdKey(ref msg, keyData); 
       } 

       break; 

      case Keys.Control |Keys.U: 

       if (this._condition2) 
       { 
        return true; 
       } 
       else 
       { 
        return base.ProcessCmdKey(ref msg, keyData); 
       } 

       break; 

      default: 

       return base.ProcessCmdKey(ref msg, keyData); 
     } 

     return true; 

Me da "código inalcanzable detectado" de advertencia en los descansos.

¿Es una buena práctica no utilizar el operador de interrupción aquí? No quiero desactivar la advertencia de "código inalcanzable detectado".

PD: Hay muchas caja en mi método ProcessCmdKey.

Respuesta

18

Hay tres instrucciones inalcanzables en su código, las dos primeras son las sentencias de interrupción y la última en la última línea "return true" también es inalcanzable, no sé si el compilador de C# lo detecta o no, pero lógicamente hay de ninguna manera también se alcanzará la declaración del último retorno.

Hay varias maneras de resolver este problema,

  1. tienda una variable temporal, llamado bool RetVal, mantenga RetVal y romper su caso interruptor y al final del retorno de la función RetVal.
  2. Si devuelve el valor antes del descanso, la declaración de interrupción es inútil.

Mejor Diseño Manera

Si devuelve valores dentro de las carcasas de interruptor, puede ser difícil de analizar su código más tarde por usted u otra persona, por lo general, es mejor mantener una variable temp valor de retorno y devolverlo al final de la función, es más fácil depurar y comprender el código para el nuevo codificador.

El cambio puede ser complicado, y puede que haya más devoluciones en el conmutador que no tengan un mejor control, si desea implementar el registro, la depuración, las devoluciones de casos podrían ser complicadas. Y se convierte en una forma difícil de navegar los gráficos de flujo lógico.

Por lo tanto, es mejor evitar el retorno desde la caja, pero igual depende de las situaciones, es necesario tomar una decisión inteligente aquí.

+4

Si los casos de su 'interruptor' son tan largos que se vuelven difíciles de analizar cuando se utilizan declaraciones 'de retorno', entonces los casos de 'cambio' son demasiado largos. Es discutible que usar un valor de temperatura y regresar al final sea al menos tan confuso como múltiples devoluciones, y probablemente aún más. –

+1

No estoy de acuerdo. Almacenar el resultado en un local hace que su código parezca más complejo de lo que realmente es; los retornos de funciones medias son una gran manera de limpiar el código. –

+0

@Carl, @Kyralessa, por favor lea la última línea, "Pero igual depende de las situaciones también, uno necesita tomar una decisión inteligente aquí", esto ya incluye casos excepcionales que ambos han mencionado, sin embargo, según mi experiencia, tengo La función media encontrada es muy mala para analizar el código en fechas posteriores. Pero esta no es una regla estándar. –

3

Utiliza el retorno en ambas condiciones. Entonces el descanso podría ser eliminado fácilmente.

Pero prefiero dejarlo allí en caso de que cambie su código alguna vez.

2

La declaración de ruptura nunca se ejecutará, porque regresa del método. Entonces sugiero eliminar el descanso innecesario.

18

break no es necesario si todas las rutas en una instrucción case finalizan con return. No lo use entonces, de lo contrario recibirá la advertencia mencionada.

+1

Sí, pero ¿es una buena práctica? o es una práctica estándar en tales casos? –

+5

@alex: Bueno, el compilador te dice que es una buena práctica, al advertirte cuando no la sigues. – Gorpik

5

No hay nada de malo en eliminar la declaración break aquí.

9

En este caso, la buena en mi humilde opinión la práctica sería acabar cada caso con un retorno, así:

case Keys.Alt|Keys.D1: 
    bool result; 
    if (this._condition1) 
     { 
      result = true; 
     } 
     else 
     { 
      result = base.ProcessCmdKey(ref msg, keyData); 
     } 
    return result; 

O

case Keys.Alt|Keys.D1: 
    bool result = (this._condition1) 
     ? true 
     : base.ProcessCmdKey(ref msg, keyData); 
    return result; 
+0

Sí, es una buena solución. –

+0

Bueno, gracias: D – Hinek

-1

De acuerdo con su código, todo el descanso (s) y nunca se llega a la última declaración, ya que antes están las declaraciones de devolución.

Se podría volver a escribir el código de la siguiente manera:

switch (keyData) 
    { 
     case Keys.Alt|Keys.D1: 
      if (this._condition1) return true; 
      else goto default; 

     case Keys.Control |Keys.U: 
      if (this._condition2) return true; 
      else goto default; 

     default: 
      return base.ProcessCmdKey(ref msg, keyData); 
    } 
+0

Eso me lastima los ojos. – Paddy

+1

Uso innecesario de 'goto': ¡No quiero! –

+0

+1 Quitaría el 'else' también, dejando simplemente goto default :. – kenny

1

amarga experiencia me ha enseñado que siempre incluyo romper declaraciones a menos que realmente quieren decir a fallthrough a la siguiente instrucción e incluso entonces, comentarlo. De lo contrario, una función podría comportarse de manera muy diferente porque otro desarrollador cambió algo tarde un viernes por la tarde y no vio el corte faltante.

Si la función, por grande que sea, se ajusta a lo mismo si ... devuelve ... más ... devuelve la estructura, puede definir una variable de código de retorno al inicio de la función. Luego, asígnelo en su declaración de caso y devuélvalo al final, cualquiera sea el valor que resulte.

+1

En C#, se prohíbe caer en las declaraciones de casos; el compilador levantará un escándalo si una ruta lógica puede pasar al siguiente caso. –

+0

Esto no es un problema con C#, ya que el compilador fallará en todos los fallthroughs automáticos. –

+0

Ah gotcha. Mi comentario es redundante para C# entonces. – tridian

0

Una sentencia switch aquí no es necesario en absoluto, evitando el problema por completo:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
{ 
    return 
     (keyData == Keys.Alt|Keys.D1 && this._condition1) || 
     (keyData == Keys.Control|Keys.U && this._condition2) || 
     base.ProcessCmdKey(ref msg, keyData); 
} 
+0

Como mencioné, hay muchos casos en el método ProcessCmdKEy ... –

+0

No hay límite en cuanto a la cantidad de condiciones que se pueden unir, ¿pero supongo que no desea cambiar demasiado el código existente? – Polyfun

+0

Quiero decir que muchos "si" serían menos comprensibles e ilegibles. La solución con "caja" será más readalbe. –

1

Se podría volver a escribir el código a ser mucho más corto y menos repatative:

bool ret = false; 
switch(keyDate){ 
    case Keys.Alt | Keys.D1: 
     ret = this._condition1; 
    break; 
    case Keys.Control |Keys.U: 
     ret = this._condition2; 
    break; 
    default: break; 
} 
return ret || base.ProcessCmdKey(ref msg, keyData); 
+0

No veo qué errores se evitarían al dejarlos en una declaración de interrupción inalcanzable. Si fuera C o C++, entonces seguro, pero C# no permite pasar de un caso al siguiente. Si necesita romper más tarde, el compilador se quejará de eso en ese momento. Y en el código actual con el que estás trabajando, es completamente inútil. –

+0

Sí, no me di cuenta de que el compilador de C# no permite el fracaso. –

3

Así como eliminando las líneas break; de allí, también podría eliminar las declaraciones else. No es necesario porque regresa del primer if.

lo que el código podría tener este lugar:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
{ 
    switch (keyData) 
    { 
     case Keys.Alt|Keys.D1: 
      if (this._condition1) 
       return true;     

      return base.ProcessCmdKey(ref msg, keyData); 

     case Keys.Control |Keys.U: 
      if (this._condition2) 
       return true; 

      return base.ProcessCmdKey(ref msg, keyData); 

     default: 
      return base.ProcessCmdKey(ref msg, keyData); 
    } 

    return true; 
} 

Usted puede adelgazar abajo aún más mediante la eliminación de sus return true; líneas e invirtiendo sus sentencias if, debido a que regrese verdadero y el final del método de todos modos:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
{ 
    switch (keyData) 
    { 
     case Keys.Alt|Keys.D1: 
      if (!this._condition1) 
       return base.ProcessCmdKey(ref msg, keyData); 

      break; 

     case Keys.Control |Keys.U: 
      if (!this._condition2) 
       return base.ProcessCmdKey(ref msg, keyData); 

      break; 

     default: 
      return base.ProcessCmdKey(ref msg, keyData); 
    } 

    return true; 
} 

EDIT: me olvidó que no se puede caer a través de uso de C# por lo que había necesidad de un descanso; en cada caso. Que tipo de ruinas la buena legibilidad de ese bloque.

+0

No importa lo que dije, aparentemente el compilador de C# se niega a caerse, –

+2

En realidad, no hay ningún problema con que sea C#. sin embargo, el segundo caso requiere 'break's al final de cada caso, o el compilador dirá no – Sekhat

+0

Tiene razón, me olvidé por completo de la regla de no caer en C#. Editaré la respuesta. – lxalln

1

Existe un cierto malentendido de que todos los bloques case en un C# switch deben terminar con un break. De hecho, deben terminar con una declaración de salto en todas las rutas de código; esto puede ser break, return, goto o incluso continue (o throw, por supuesto). Como el compilador generará un error si hay una ruta de código sin declaración de salto, no hay absolutamente ninguna razón para el break final en el ejemplo. El compilador no le permitirá cambiar su código a una versión donde se alcanzará el break final, y este sería el momento de agregarlo.

+1

O " while true() {} "! –

0

Como el código es actualmente, los comandos "break" nunca se ejecutarán ni el último comando "return true". Eliminar estos eliminaría las advertencias.

Es posible que desee intentar una solución con menos rutas de retorno, ya que puede hacer que el código sea más difícil de depurar y comprender.Algo como esto:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
    { 
     bool processed = false; 
     switch (keyData) 
     { 
      case Keys.Alt | Keys.D1: 

       if (this._condition1) 
        processed = true; 

       break; 

      case Keys.Control | Keys.U: 

       if (this._condition2) 
        processed = true; 

       break; 
     } 

     if (!processed) 
      processed = base.ProcessCmdKey(ref msg, keyData); 

     return processed; 
    } 
0

entiendo que esto no responde a la pregunta directamente, pero inspirado por las diversas respuestas aquí sólo quería añadir otra variación sobre cómo el "interruptor" podría estructurarse:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
{ 
    if (keyData == Keys.Alt|Keys.D1 && _condition1) 
     return true; 

    if (keyData == Keys.Control|Keys.U && _condition2) 
     return true; 

    // ...repeat for other variations 

    return base.ProcessCmdKey(ref msg, keyData); 
} 
Cuestiones relacionadas