solo - Legible o no: C#mĂșltiples operadores ternarios+Tirar si no coincide
operadores logicos c# ejemplos (15)
¿Por qué no usar un bool de tipo anulable?
private bool? CanExecuteAdd(string parameter) {
return
this.Script == null ? false
: parameter == "Step" ? true
: parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
: parameter == "Choice" ? this.SelectedElement != null
: parameter == "Jump" ? this.SelectedStep != null
: parameter == "Conditional jump" ? false
: null;
}
¿Encuentra el siguiente código C # legible?
private bool CanExecuteAdd(string parameter) {
return
this.Script == null ? false
: parameter == "Step" ? true
: parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
: parameter == "Choice" ? this.SelectedElement != null
: parameter == "Jump" ? this.SelectedStep != null
: parameter == "Conditional jump" ? false
: false.Throw("Unknown Add parameter {0} in XAML.".F(parameter));
}
donde Throw se define como:
public static T Throw<T>(this T ignored, string message) {
throw new Exception(message);
}
Sé que eso no es idiomático C #. Sin embargo, ¿podría entenderlo en primera o segunda mirada? ¿O me desvié demasiado?
¿Por qué no usar un interruptor? Creo que es mucho más legible.
private bool CanExecuteAdd(string parameter) {
if (Script == null)
return false;
switch (parameter) {
case "Step":
return true;
case "Element":
return ElementSelectedInLibrary != null && SelectedStep != null;
case "Choice":
return SelectedElement != null;
case "Jump":
return SelectedStep != null;
case "Conditional jump":
return false;
default:
throw new Exception(string.Format("Unknown Add parameter {0} in XAML.", parameter));
}
}
Al principio me horroricé, pero en realidad no puedo pensar en una manera de escribir esto mucho más claro en C # - Estaba tratando de pensar en algo en el que tuvieras asignada una matriz de Funcs a los resultados, pero se puso aún más feo.
Aunque analizar los condicionales reales es difícil, al menos es fácil asimilar el intento, aunque preferiría usar un interruptor y manejar todo lo demás como un caso especial.
Convierta su ternario anidado en un interruptor. Nunca fuerce a una estructura de control a hacer deficiente o ilegítimamente lo que una estructura integrada hará perfectamente, especialmente si no hay un beneficio obvio.
Demasiado difícil de leer, primero ocúpese de las excepciones.
Maneje cada caso por su cuenta si, entonces puede tener condiciones más complejas.
Esta es una de las pocas veces, esta cantidad de retornos por separado en un método sería aceptable
private bool CanExecuteAdd(string parameter)
{
if (this.Script == null)
return false;
if (parameter.NotIn([] {"Step", "Element", "Choice", "Jump", "Conditional jump"})
throw new Exception("Unknown Add parameter {0} in XAML.".F(parameter));
if (parameter == "Step")
return true;
if (parameter == "Element")
this.ElementSelectedInLibrary != null && this.SelectedStep != null;
// etc, etc
}
Ah, y .NotIn es un método de extensión, lo opuesto a esto, me imagino (no puedo decir que esto sea bastante exacto a lo que se necesita)
public static bool In<T>(this T obj, IEnumerable<T> arr)
{
return arr.Contains(obj);
}
Desafortunadamente, el operador ternario (? :) no es un idioma común en los lenguajes C. He conocido a muchos desarrolladores de C, C ++ y C # que tienen que hacer una pausa para leerlo porque no están familiarizados con él o no lo hacen. usarlo Eso no lo convierte en una función de lenguaje incorrecto o ilegible, sin embargo, esos mismos desarrolladores pueden llamar al ejemplo de OP ilegible porque anida una función de idioma con la que no se sienten cómodos.
No encuentro el ejemplo ilegible: he visto operadores ternarios anidados muchas veces. Sin embargo, creo que usar un conmutador sería una opción preferible para verificar el ''parámetro'' contra las cadenas.
Mucho más irritante para mí es ese método de extensión Throw que ignora el parámetro ''this''. ¿Qué significaría 42.Trow (...)? Si estuviera revisando el código, lo llamaría un mal diseño.
En realidad, nunca he visto al operador ternario expulsado hasta ahora. Sin embargo, entendí a dónde ibas. Además, estoy de acuerdo con Jon, no me gusta lo falso. Parte del momento.
He usado este tipo de código en Java una buena cantidad. No me gusta lo false.Throw
la false.Throw
, pero tener múltiples condicionales como este (especialmente formateado de esta manera) está bien en mi opinión.
Es un poco extraño la primera vez que lo ves, pero después de eso es solo un patrón útil para saber.
Una alternativa al uso de false.Throw
aquí sería algo como esto:
bool? ret = this.Script == null ? false
: parameter == "Step" ? true
: parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
: parameter == "Choice" ? this.SelectedElement != null
: parameter == "Jump" ? this.SelectedStep != null
: parameter == "Conditional jump" ? false
: null;
if (ret == null)
{
throw new ArgumentException(
string.Format("Unknown Add parameter {0} in XAML.", parameter);
}
return ret.Value;
EDITAR: En realidad, en este caso no usaría if / else ni este patrón ... Usaría el interruptor / caja. Esto puede ser muy compacto si lo desea:
if (this.Script == null)
{
return false;
}
switch (parameter)
{
case "Step": return true;
case "Element": return this.ElementSelectedInLibrary != null && this.SelectedStep != null;
case "Choice": return this.SelectedElement != null;
case "Jump": return this.SelectedStep != null;
default: throw new ArgumentException(
string.Format("Unknown Add parameter {0} in XAML.", parameter);
}
Me parece bien, pero alteraría tu método Throw para:
static TObject Throw<TObject, TException>(this TObject ignored, TException exception)
{
throw exception;
}
Eso le permite especificar el tipo de excepción lanzada.
Mi regla de oro: usar expresiones para cosas sin efectos secundarios. Use declaraciones para cosas con un efecto secundario y para flujo de control.
Lanzar es efectivamente un efecto secundario; no calcula un valor, altera el flujo de control. Está calculando un valor, computación, computación, computación y, luego, auge, efecto secundario. Me parece que un código como ese es confuso y molesto. Yo digo que el flujo de control debería estar en enunciados, no en el efecto secundario de algo que parece un cálculo.
Realmente no me gusta este código. Me llevó más de 15 segundos entenderlo, así que me di por vencido.
Un si / entonces sería preferible.
Ser no idiomático significa que estás obligando al lector a pasar tiempo pensando si lo que está leyendo significa o no lo que cree que significa.
Entonces, ser legible no le gusta mucho al lector sofisticado (es decir, sospechoso). Esto me parece un caso de ser inteligente por el simple hecho de ser inteligente.
¿Hay alguna razón para no usar un interruptor o una construcción de otro tipo aquí?
Yo voto por no legible.
Aunque la sintaxis es correcta, es un poco intrincada y como no es así, me atrevería a decir, "tradicional", muchos desarrolladores tendrán que perder el tiempo tratando de asegurarse de que entienden lo que están leyendo. No es una situación ideal.
La legibilidad es definitivamente un ingrediente clave para una buena codificación, y yo diría que su muestra no es inmediatamente legible para la mayoría de los desarrolladores.
En C & C ++, el uso descrito es idiomático y la razón del operador. El beneficio del condicional ternario vs. un if-entonces-else encadenado es que es una expresión con un tipo conocido. En C ++, puedes escribir
foo_t foo = bar_p ? bar
: qux_p ? qux
: woz_p ? woz
: (throw SomeExpression(), foo_t())
;
Observe el operador de coma, que devuelve un foo_t que nunca se construirá debido al lanzamiento.
Me gusta el operador condicional y lo uso mucho.
Este ejemplo es un poco confuso, porque la naturaleza del operador no está clara desde el diseño y uso.
Por lo menos, me gustaría aclarar la elección y las alternativas al usar este formato:
choice
? true-case
: false-case
Pero si aplicamos eso a su código, revela la falta de claridad en el uso de la construcción de esta manera:
return
this.Script == null
? false
: parameter == "Step"
? true
: parameter == "Element"
? this.ElementSelectedInLibrary != null && this.SelectedStep != null
: parameter == "Choice"
? this.SelectedElement != null
: parameter == "Jump"
? this.SelectedStep != null
: parameter == "Conditional jump"
? false
: false.Throw("Unknown Add parameter {0} in XAML.".F(parameter));
Esto me parece que estamos tratando de usar el operador condicional como una declaración de cambio, donde una declaración de cambio, o mejor aún un patrón de diseño como el Patrón de comando , sería mucho más claro.