c# - ternario - if/else, buen diseño
switch c# (20)
Creo que el Tipo Class1 debería determinar si puede hacerlo, dado un valor SomeEnum.
Dejaría la decisión sobre si puede o no manejar la información para que decida:
bool TryDo(Class1 obj, SomeEnum type)
{
return obj.Do(type));
}
¿Es aceptable / buen estilo para simplificar esta función?
bool TryDo(Class1 obj, SomeEnum type)
{
if (obj.CanDo(type))
{
return Do(obj);
}
else
{
return false;
}
}
como:
bool TryDo(Class1 obj, SomeEnum type)
{
return obj.CanDo(type) && Do(obj);
}
La segunda versión es más corta, pero podría decirse que es menos intuitiva.
El else
es inútil y el &&
, sin embargo obvio, no es tan legible como el texto puro.
Prefiero lo siguiente:
bool TryDo(Class1 obj, SomeEnum type)
{
if (obj.CanDo(type))
{
return Do(obj);
}
return false;
}
El problema de efecto secundario que algunas personas están mencionando es falso. Nadie debería sorprenderse de que un método llamado "Do" tenga un efecto secundario.
El hecho es que estás llamando a dos métodos. Ambos métodos tienen bool como valor de retorno. Su segunda opción es muy clara y concisa. (Aunque me deshace del paréntesis externo y olvidas un punto y coma final).
En este caso, iría con la primera opción, es mucho más legible y la intención del código es mucho más clara.
La primera versión es mucho más fácil de leer con menos posibilidades de ser malentendida, y creo que eso es importante en el código del mundo real.
La versión abreviada oculta el hecho de que Do hace algo. Parece que estás haciendo una comparación y devolviendo el resultado, pero en realidad estás haciendo una comparación y realizando una acción, y no es obvio que el código tenga este "efecto secundario".
Creo que el núcleo del problema es que está devolviendo el resultado de una evaluación y el código de retorno de una acción. Si devolviera el resultado de dos evaluaciones de esta manera, no tendría ningún problema con eso
Lo que codificaría es:
return obj.CanDo(type) ? Do(obj) : false;
Me puede dar odio por esto, pero ¿qué hay de:
if (obj.CanDo(type)) return Do(obj);
return false;
No me gusta tener frenos para uno.
No me gusta la segunda versión, ya que realmente no soy partidario de aprovechar el orden de evaluación de la sub-expresión en una expresión condicional. Está colocando un orden esperado en sub-expresiones que, en mi opinión, deberían tener la misma precedencia (aunque no lo hagan).
Al mismo tiempo, creo que la primera versión está un poco hinchada, así que optaría por la solución ternaria que considero altamente legible.
No. La segunda versión
return (obj.CanDo(type) && Do(obj))
se basa en el comportamiento de cortocircuito del operador && que es una optimización, no un método para controlar el flujo del programa. En mi opinión, esto es solo un poco diferente que usar excepciones para el flujo del programa (y casi tan malo).
Odio el código inteligente, es una perra para comprender y depurar. El objetivo de la función es "si podemos hacer esto, luego hazlo y devuelve el resultado; de lo contrario, devuelve falso". El código original hace que el significado sea muy claro.
Nunca hagas eso. Mantenlo simple e intuitivo.
OMI solo está bien si la segunda función no tiene efectos secundarios. Pero como Do () tiene efectos secundarios, yo elegiría el if.
Mi guía es que una expresión no debe tener efectos secundarios. Cuando llame a funciones con un efecto secundario, use una declaración.
Esta guía tiene un problema si una función devuelve un código de falla. En ese caso, acepto la asignación de ese código de error a una variable o lo devuelvo directamente. Pero no uso el valor de retorno en una expresión compleja. Entonces, quizás debería decir que solo la función externa llamada en una expresión debería tener un efecto secundario.
Vea el blog de Eric Lippert para una explicación más larga.
Otra alternativa que puede ser un poco más legible es usar el operador condicional:
bool TryDo(Class1 obj, SomeEnum type) {
return obj.CanDo(type) ? Do(obj) : false;
}
Para mí, prefiero el segundo método. La mayoría de mis métodos que devuelven bool se acortan de la misma manera cuando se trata de una lógica condicional simple.
Sí.
Especialmente con nombres similares a sus nombres elegidos, es decir, CanDoSomething
y DoSomething
, es absolutamente claro para cualquier programador competente lo que hace el segundo código: " si y solo si la condición se cumple, haga algo y devuelva el resultado". "Si y solo si" es el significado central del operador &&
cortocircuitado.
El primer código es intrincado e innecesariamente largo sin dar más información que el segundo código.
Pero en general, las dos condiciones pueden no formar una relación tan íntima (como en CanDo
y Do
) y sería mejor separarlas lógicamente porque ponerlas en el mismo condicional podría no tener un sentido intuitivo.
Mucha gente dice que la primera versión es "mucho más clara". Realmente me gustaría escuchar sus argumentos. No puedo pensar en ninguno.
Por otro lado, está este código estrechamente relacionado (aunque no exactamente el mismo):
if (condition)
return true;
else
return false;
esto siempre debería transformarse en esto:
return condition;
No hay excepción Es conciso y aún más legible para alguien que es competente en el lenguaje.
Si bien está claro cuál es el segundo código para un programador competente, me parece más claro y más fácil leer en un caso más general escribir el código como cualquier otro estilo "si se cumple la precondición, hacer acción, de lo contrario fallar".
Esto podría lograrse ya sea por:
return obj.CanDo(type)? Do(obj) : false;
O,
if(obj.CanDo(type)) return Do(obj);
return false;
Encuentro esto superior porque este mismo estilo se puede replicar sin importar el tipo de devolución. Por ejemplo,
return (array.Length > 1)? array[0] : null;
O,
return (curActivity != null)? curActivity.Operate() : 0;
El mismo estilo también se puede expandir a situaciones que no tienen un valor de retorno:
if(curSelection != null)
curSelection.DoSomething();
Mis dos centavos.
Tampoco, porque cuando TryDo devuelve False, no puede determinar si fue porque ''No CanDo'' o ''Do return False''.
Entiendo completamente que puedes ignorar el resultado, pero la forma en que se expresa implica que el resultado tiene significado.
Si el resultado no tiene sentido, la intención sería más clara con
void TryDo(Class1 obj, SomeEnum type)
{
if (obj.CanDo(type))
Do(obj);
return;
}
Si el resultado tiene un significado, entonces debería haber una manera de diferenciar entre los dos retornos "falsos". IE ¿Qué significa ''If (! TryDo (x))''?
Editar: para decirlo de otra manera, el código del OP dice que ''No puedo nadar'' es lo mismo que ''Intenté nadar y ahogarme''
Versión con paréntesis:
bool TryDo(Class1 obj, SomeEnum type)
{
if (obj.CanDo(type))
{
return Do(obj);
}
return false;
}
O versión sin corchetes (en los comentarios de respuesta hay un alto debate al respecto):
bool TryDo(Class1 obj, SomeEnum type)
{
/*
* If you want use this syntax of
* "if", this doing this on self
* responsibility, and i don''t want
* get down votes for this syntax,
* because if I remove this from my
* answer, i get down votes because many
* peoples think brackets i wrong.
* See comments for more information.
*/
if (obj.CanDo(type))
return Do(obj);
return false;
}
Su primer ejemplo de código es mejor, pero creo que mi versión es aún mejor.
Su segunda versión no es fácil de leer y hace que el código sea más difícil de mantener , esto es malo.
Ya hay varias buenas respuestas, pero pensé que mostraría un ejemplo más de (lo que considero) código bueno y legible.
bool TryDo(Class1 obj, SomeEnum type)
{
bool result = false;
if (obj.CanDo(type))
{
result = Do(obj);
}
return result;
}
Mantenga o quite las corcheas alrededor del cuerpo de la declaración if según el gusto.
Me gusta este enfoque porque ilustra que el resultado es false
menos que ocurra algo más, y creo que muestra más claramente que Do()
está haciendo algo y devuelve un valor booleano, que TryDo()
usa como valor de retorno.
No me gusta este diseño, y tal vez no por la razón obvia. Lo que me molesta es devolver Do (obj); Para mí no tiene sentido que la función Do tenga un tipo de retorno bool. ¿Es esto un sustituto de los errores de empuje de propiedades? Lo más probable es que esta función sea nula o devuelva un objeto complejo. El escenario simplemente no debería aparecer. Además, si un bool de alguna manera tiene sentido ahora, fácilmente puede dejar de tener sentido en el futuro. Con el cambio de código, requerirá más re factorización para corregir