Tengo un método donde toda la lógica se realiza dentro de un bucle foreach que itera sobre el parámetro del método:
public IEnumerable<TransformedNode> TransformNodes(IEnumerable<Node> nodes)
{
foreach(var node in nodes)
{
// yadda yadda yadda
yield return transformedNode;
}
}
En este caso, enviar una colección vacía da como resultado una colección vacía, pero me pregunto si eso no es prudente.
Mi lógica aquí es que si alguien llama a este método, entonces tienen la intención de pasar datos, y solo pasarían una colección vacía a mi método en circunstancias erróneas.
¿Debería detectar este comportamiento y lanzar una excepción, o es la mejor práctica devolver la colección vacía?
c#
exceptions
collections
parameters
Nick Udell
fuente
fuente
null
pero no si está vacía.Respuestas:
Los métodos de utilidad no deberían arrojar colecciones vacías. Sus clientes API lo odiarían por ello.
Una colección puede estar vacía; una "colección que no debe estar vacía" es conceptualmente una cosa mucho más difícil de trabajar.
La transformación de una colección vacía tiene un resultado obvio: la colección vacía. (Incluso puede guardar algo de basura devolviendo el parámetro en sí).
Hay muchas circunstancias en las que un módulo mantiene listas de cosas que pueden o no estar llenas de algo. Tener que verificar el vacío antes de cada llamada
transform
es molesto y tiene el potencial de convertir un algoritmo simple y elegante en un desastre feo.Los métodos de utilidad siempre deben esforzarse por ser liberales en sus entradas y conservadores en sus salidas.
Por todas estas razones, por el amor de Dios, maneje la colección vacía correctamente. Nada es más exasperante que un módulo auxiliar que cree que sabe lo que quiere mejor que usted.
fuente
null
a la colección vacía. Las funciones con limitaciones implícitas son un dolor.Populate1(input); output1 = TransformNodes(input); Populate2(input); output2 = TransformNodes(input);
si Populate1 dejara la colección vacía y la devolvieras para la primera llamada TransformNodes, output1 y input serían la misma colección, y cuando se llamaba a Populaten2 si ponía nodos en la colección, terminarías con tu segundo conjunto de entrada en salida2.Veo dos preguntas importantes que determinan la respuesta a esto:
1. Retorno significativo
Esencialmente, si puede devolver algo significativo, no arroje una excepción. Deje que la persona que llama lidie con el resultado. Entonces, si tu función ...
En general, mi sesgo de FP me dice "devolver algo significativo" y nulo puede tener un significado válido en este caso.
2. Estilo
¿Su código general (o el código del proyecto o el código del equipo) favorece un estilo funcional? Si no, se esperarán y se tratarán excepciones. En caso afirmativo, considere devolver un Tipo de opción . Con un tipo de opción, puede devolver una respuesta significativa o Ninguno / Nada . En mi tercer ejemplo de arriba, Nothing sería una buena respuesta en estilo FP. El hecho de que la función devuelva señales de tipo Opción claramente a la persona que llama que una respuesta significativa puede no ser posible y que la persona que llama debe estar preparada para lidiar con eso. Siento que le da a la persona que llama más opciones (si perdona el juego de palabras).
F # es donde todos los niños del fresco .Net hacen este tipo de cosas, pero C # hace apoyar este estilo.
tl; dr
Mantenga excepciones para errores inesperados en su propia ruta de código, entrada no totalmente previsible (y legal) de otra persona.
fuente
Como siempre, depende.
¿ Importa que la colección esté vacía?
La mayoría del código de manejo de colecciones probablemente diría "no"; una colección puede tener cualquier número de elementos, incluido cero.
Ahora, si tiene algún tipo de colección donde es "inválido" no tener elementos, entonces ese es un nuevo requisito y tiene que decidir qué hacer al respecto.
Pide prestada alguna lógica de prueba del mundo de la base de datos: prueba para cero elementos, un elemento y dos elementos. Eso atiende a los casos más importantes (eliminar cualquier condición de unión cartesiana o interna mal formada).
fuente
java.util.Collection
personalizada, sino unacom.foo.util.NonEmptyCollection
clase personalizada , que pueda preservar constantemente esta invariante y evitar que comience a un estado no válido.Como cuestión de buen diseño, acepte tantas variaciones en sus entradas como sea práctico o posible. Solo se deben generar excepciones cuando (se presenta una entrada inaceptable O se producen errores inesperados durante el procesamiento) Y, como resultado, el programa no puede continuar de una manera predecible .
En este caso, debe esperarse que se presente una colección vacía, y su código debe manejarla (lo que ya hace). Sería una violación de todo lo que es bueno si su código arrojó una excepción aquí. Esto sería similar a multiplicar 0 por 0 en matemáticas. Es redundante, pero absolutamente necesario para que funcione como lo hace.
Ahora, al argumento de colección nula. En este caso, una colección nula es un error de programación: el programador olvidó asignar una variable. Este es un caso en el que podría lanzarse una excepción, porque no puede procesar eso significativamente en una salida, e intentar hacerlo introduciría un comportamiento inesperado. Esto sería similar a dividir entre cero en matemáticas: no tiene ningún sentido.
fuente
La solución correcta es mucho más difícil de ver cuando solo está mirando su función de forma aislada. Considere su función como parte de un problema mayor . Una posible solución a ese ejemplo se ve así (en Scala):
Primero, divide la cadena por no dígitos, filtra las cadenas vacías, convierte las cadenas en enteros y luego filtra para mantener solo los números de cuatro dígitos. Su función podría estar
map (_.toInt)
en la tubería.Este código es bastante sencillo porque cada etapa de la tubería solo maneja una cadena vacía o una colección vacía. Si coloca una cadena vacía al principio, obtendrá una lista vacía al final. No tiene que detenerse y verificar
null
o una excepción después de cada llamada.Por supuesto, eso supone que una lista de salida vacía no tiene más de un significado. Si necesita diferenciar entre una salida vacía causada por una entrada vacía y una causada por la propia transformación, eso cambia completamente las cosas.
fuente
Esta pregunta es en realidad sobre excepciones. Si lo mira de esa manera e ignora la colección vacía como un detalle de implementación, la respuesta es sencilla:
1) Un método debe arrojar una excepción cuando no puede continuar: no puede realizar la tarea designada o devolver el valor apropiado.
2) Un método debe detectar una excepción cuando puede continuar a pesar de la falla.
Por lo tanto, su método auxiliar no debería ser "útil" y generar una excepción a menos que no pueda hacer su trabajo con una colección vacía. Deje que la persona que llama determine si se pueden manejar los resultados.
Si devuelve una colección vacía o nula, es un poco más difícil pero no mucho: las colecciones anulables deben evitarse si es posible. El propósito de una colección anulable sería indicar (como en SQL) que no tiene la información; una colección de niños, por ejemplo, podría ser nula si no sabe si alguien tiene alguna, pero usted no Sé que no lo hacen. Pero si eso es importante por alguna razón, probablemente valga una variable adicional para rastrearlo.
fuente
El método se nombra
TransformNodes
. En el caso de una colección vacía como entrada, recuperar una colección vacía es natural e intuitivo, y tiene un sentido matemático perfecto.Si el método fue nombrado
Max
y diseñado para devolver el elemento máximo, entonces sería natural lanzarNoSuchElementException
una colección vacía, ya que el máximo de nada no tiene sentido matemático.Si el método fue nombrado
JoinSqlColumnNames
y diseñado para devolver una cadena donde los elementos están unidos por una coma para usar en consultas SQL, entonces tendría sentido lanzarIllegalArgumentException
una colección vacía, ya que el llamador eventualmente obtendría un error SQL de todos modos si usara la cadena en una consulta SQL directamente sin más comprobaciones, y en lugar de buscar una cadena vacía devuelta, realmente debería haber verificado la colección vacía.fuente
Max
de nada es a menudo negativo infinito.Retrocedamos y usemos un ejemplo diferente que calcule la media aritmética de una matriz de valores.
Si la matriz de entrada está vacía (o nula), ¿puede cumplir razonablemente la solicitud de la persona que llama? No. ¿Cuáles son tus opciones? Bueno, podrías:
Les digo que les den el error si le han dado una entrada no válida y la solicitud no se puede completar. Me refiero a un error grave desde el primer día para que entiendan los requisitos de su programa. Después de todo, su función no está en condiciones de responder. Si la operación pudiera fallar (por ejemplo, copiar un archivo), entonces su API debería darles un error con el que puedan lidiar.
De modo que eso puede definir cómo su biblioteca maneja las solicitudes con formato incorrecto y las solicitudes que pueden fallar.
Es muy importante que su código sea coherente en la forma en que maneja estas clases de errores.
La siguiente categoría es decidir cómo su biblioteca maneja las solicitudes sin sentido. Volviendo a un ejemplo similar a la suya - Vamos a utilizar una función que determina si existe un archivo en una ruta:
bool FileExistsAtPath(String)
. Si el cliente pasa una cadena vacía, ¿cómo maneja este escenario? ¿Qué tal una matriz vacía o nula pasadavoid SaveDocuments(Array<Document>)
? Decida su biblioteca / base de código, y sea coherente. Considero que estos casos son errores y prohíbo que los clientes realicen solicitudes sin sentido al marcarlos como errores (a través de una aserción). Algunas personas resistirán fuertemente esa idea / acción. Encuentro esta detección de errores muy útil. Es muy bueno para localizar problemas en los programas, con buena ubicación para el programa infractor. Los programas son mucho más claros y correctos (considere la evolución de su base de código), y no graben ciclos dentro de funciones que no hacen nada. El código es más pequeño / más limpio de esta manera, y las comprobaciones generalmente se envían a los lugares donde se puede presentar el problema.fuente
if (!FileExists(path)) { ...create it!!!... }
errores, muchos de los cuales se detectarían antes de cometer, si las líneas de corrección no fueran borrosas.Como regla general, una función estándar debería poder aceptar la lista más amplia de entradas y dar retroalimentación sobre ella, hay muchos ejemplos en los que los programadores usan funciones de formas que el diseñador no había planeado, teniendo esto en cuenta, creo que la función debería poder aceptar no solo colecciones vacías, sino también una amplia gama de tipos de entrada y devolver comentarios con gracia, ya sea incluso un objeto de error de cualquier operación realizada en la entrada ...
fuente
writePaycheckToEmployee
no debería aceptar un número negativo como entrada ... pero si "retroalimentación" significa "hace algo que podría suceder después", entonces sí, cada función hará algo que haga después.