¿Cuál es la mejor práctica para la validación de parámetros de constructor?
Supongamos un simple bit de C #:
public class MyClass
{
public MyClass(string text)
{
if (String.IsNullOrEmpty(text))
throw new ArgumentException("Text cannot be empty");
// continue with normal construction
}
}
¿Sería aceptable lanzar una excepción?
La alternativa que encontré fue la validación previa, antes de instanciar:
public class CallingClass
{
public MyClass MakeMyClass(string text)
{
if (String.IsNullOrEmpty(text))
{
MessageBox.Show("Text cannot be empty");
return null;
}
else
{
return new MyClass(text);
}
}
}
c#
validation
constructors
MPelletier
fuente
fuente
Respuestas:
Tiendo a realizar toda mi validación en el constructor. Esto es imprescindible porque casi siempre creo objetos inmutables. Para su caso específico, creo que esto es aceptable.
Si está utilizando .NET 4, puede hacerlo. Por supuesto, esto depende de si considera que una cadena que contiene solo espacios en blanco no es válida.
fuente
Init
para recibir algún tipo de código de error cuando una excepción funcione igual de bien y obligue a su objeto a mantener siempre un estado válido?ArgumentNullException
y otra que prueba el vacío y arroja unaArgumentException
. Permite que la persona que llama sea más exigente si lo desea en su manejo de excepciones.Mucha gente dice que los constructores no deberían lanzar excepciones. KyleG en esta página , por ejemplo, hace exactamente eso. Honestamente, no puedo pensar en una razón por la que no.
En C ++, lanzar una excepción de un constructor es una mala idea, porque te deja con memoria asignada que contiene un objeto no inicializado al que no tienes referencia (es decir, es una pérdida de memoria clásica). Probablemente de ahí proviene el estigma: un grupo de desarrolladores de C ++ de la vieja escuela analizó a medias su aprendizaje de C # y simplemente aplicó lo que sabían de C ++. Por el contrario, en Objective-C Apple separó el paso de asignación del paso de inicialización, por lo que los constructores en ese lenguaje pueden lanzar excepciones.
C # no puede perder memoria de una llamada fallida a un constructor. Incluso algunas clases en .NET Framework arrojarán excepciones en sus constructores.
fuente
Lanzar una excepción IFF la clase no puede ponerse en un estado consistente con respecto a su uso semántico. De lo contrario no lo hagas. NUNCA permita que exista un objeto en un estado inconsistente. Esto incluye no proporcionar constructores completos (como tener un constructor vacío + initialize () antes de que su objeto esté realmente completamente construido) ... ¡SOLO DIGA NO!
En caso de apuro, todos lo hacen. Lo hice el otro día para un objeto de uso muy limitado dentro de un alcance estrecho. Algún día en el futuro, yo u otra persona probablemente pagaremos el precio de ese desliz en la práctica.
Debo señalar que por "constructor" me refiero a lo que el cliente llama para construir el objeto. Eso podría ser fácilmente algo más que una construcción real que se conoce con el nombre de "Constructor". Por ejemplo, algo así en C ++ no violaría el principio IMNSHO:
Dado que la única forma de crear un
funky_object
es llamandobuild_funky
al principio de nunca permitir que exista un objeto no válido, permanece intacto aunque el "Constructor" real no termine el trabajo.Sin embargo, eso es mucho trabajo adicional para obtener ganancias cuestionables (tal vez incluso pérdidas). Todavía prefiero la ruta de excepción.
fuente
En este caso, usaría el método de fábrica. Básicamente, establezca que su clase tenga solo constructores privados y tenga un método de fábrica que devuelva una instancia de su objeto. Si los parámetros iniciales no son válidos, simplemente devuelva nulo y haga que el código de llamada decida qué hacer.
Nunca arroje excepciones a menos que las condiciones sean excepcionales. Estoy pensando que en este caso, un valor vacío se puede pasar fácilmente. Si ese es el caso, el uso de este patrón evitaría excepciones y las penalizaciones de rendimiento en las que incurren mientras mantiene válido el estado MyClass.
fuente
Uno podría cuestionar razonablemente estas pautas y decir: "¡Pero no sigo estas reglas y mi código funciona bien!" A eso, respondería: "Bueno, eso puede ser cierto, hasta que no lo sea".
fuente
Depende de lo que esté haciendo MyClass. Si MyClass fuera en realidad una clase de repositorio de datos y el texto del parámetro fuera una cadena de conexión, la mejor práctica sería lanzar una excepción ArgumentException. Sin embargo, si MyClass es la clase StringBuilder (por ejemplo), entonces podría dejarlo en blanco.
Entonces, depende de cuán esencial sea el texto del parámetro para el método: ¿tiene sentido el objeto con un valor nulo o en blanco?
fuente
Prefiero establecer un valor predeterminado, pero sé que Java tiene la biblioteca "Apache Commons" que hace algo como esto, y creo que también es una buena idea. No veo un problema al lanzar una excepción si el valor no válido pondría el objeto en un estado inutilizable; una cadena no es un buen ejemplo, pero ¿y si fuera por la DI del pobre? No podría operar si
null
se pasara un valor de en lugar de, digamos, unaICustomerRepository
interfaz. En una situación como esta, diría que lanzar una excepción es la forma correcta de manejar las cosas.fuente
Cuando solía trabajar en c ++, no solía lanzar una excepción en el constructor debido al problema de pérdida de memoria que puede provocar, lo había aprendido por las malas. Cualquiera que trabaje con c ++ sabe lo difícil y problemática que puede ser la pérdida de memoria.
Pero si está en c # / Java, entonces no tiene este problema, porque el recolector de basura recolectará la memoria. Si está utilizando C #, creo que es preferible utilizar la propiedad de una manera agradable y coherente para garantizar que las restricciones de datos estén garantizadas.
fuente
Lanzar una excepción será un verdadero dolor para los usuarios de tu clase. Si la validación es un problema, generalmente hago que la creación del objeto sea un proceso de 2 pasos.
1 - Crear la instancia
2 - Llame a un método Initialize con un resultado de retorno.
O
Llame a un método / función que cree la clase para usted que pueda hacer la validación.
O
Tiene una bandera isValid, que no me gusta especialmente, pero algunas personas sí.
fuente
isValid = false
. Tener que probar después de crear una clase si es válido es horrible, un diseño muy propenso a errores. Y, usted dijo, tenga un constructor, luego llame a initialize ... ¿Qué pasa si no llamo initialize? ¿Entonces que? ¿Y qué pasa si Initialize obtiene datos incorrectos, puedo lanzar una excepción ahora? Tu clase no debería ser difícil de usar.