2009-09-28 29 views
39

En mi aplicación, necesito lanzar una excepción si una propiedad de una clase específica es nula o está vacía (en caso de que sea una cadena). No estoy seguro de cuál es la mejor excepción para usar en este caso. No me gustaría crear una nueva excepción y no estoy seguro si ArgumentNullException es apropiado en este caso.¿Qué tipo de excepción usar cuando una propiedad no puede ser nula?

¿Debo crear una nueva excepción o puedo hacer una excepción?

No me importa lanzar una excepción ApplicationException.

+0

Toma en cuenta también las propiedades son métodos azucaradas sintácticas. – Dykam

+0

Una discusión interesante relacionada: http://stackoverflow.com/questions/1488472/best-practices-throwing-exceptions-from-properties – Joren

Respuesta

65

Los MSDN guidelines for standard exceptions estados:

hacer en el valor de uso para el nombre del parámetro de valor implícito de propiedad set.

El siguiente ejemplo de código muestra una propiedad que arroja una excepción si la persona que llama pasa un argumento nulo.

public IPAddress Address 
{ 
    get 
    { 
     return address; 
    } 
    set 
    { 
     if(value == null) 
     { 
      throw new ArgumentNullException("value"); 
     } 
     address = value; 
    } 
} 

Además, el MSDN guidelines for property design dicen:

evitar tirar excepciones a captadores de propiedad.

Los captadores de propiedades deben ser operaciones simples sin ningún requisito previo. Si un getter puede arrojar una excepción, considere rediseñar la propiedad a sea un método. Esta recomendación no se aplica a los indexadores . Los indexadores pueden arrojar excepciones debido a argumentos no válidos.

Es válido y aceptable lanzar excepciones de un establecimiento de propiedades.

Así que suelta ArgumentNullException en el organismo en null y ArgumentException en la cadena vacía, y no hacer nada en el captador. Como el colocador lanza y solo usted tiene acceso al campo de respaldo, es fácil asegurarse de que no contenga un valor no válido. Tener el tiro de getter es inútil. Sin embargo, este podría ser un buen lugar para usar Debug.Assert.

Si realmente no puede proporcionar un valor por defecto, entonces supongamos que tiene tres opciones:

  1. sólo devuelve lo que haya en la propiedad y documentar este comportamiento como parte del contrato de uso. Deja que la persona que llama lidie con eso. También podría exigir un valor válido en el constructor. Sin embargo, esto podría ser completamente inapropiado para su aplicación.

  2. Reemplazar la propiedad por métodos: un método setter que arroja cuando pasa un valor no válido, y un método getter que arroja InvalidOperationException cuando nunca se le asignó un valor válido a la propiedad.

  3. Tirar InvalidOperationException del comprador, ya que podría considerar 'la propiedad nunca ha sido asignada' a un estado no válido. Si bien no debería tirar normalmente desde getters, supongo que esta podría ser una buena razón para hacer una excepción.

Si elige las opciones 2 o 3, también debe incluir un método que devuelve un TryGet- bool que indica si la propiedad se ha establecido en un valor válido, y si es así devuelve ese valor en un parámetro out . De lo contrario, obligará a las personas que llaman a estar preparadas para manejar un InvalidOperationException, a menos que hayan establecido previamente la propiedad ellos mismos y así sepan que no lanzará. Comparar int.Parse versus int.TryParse.

Sugeriría usar la opción 2 con el método TryGet. No infringe ninguna de las pautas e impone requisitos mínimos sobre el código de llamada.


Sobre las otras sugerencias
ApplicationException es demasiado general. ArgumentException es un poco demasiado general para null, pero está bien si no.MSDN docs again:

arroje la (la más derivada) excepción más específica que es apropiado. Por ejemplo, si un método recibe un valor nulo (Nothing en Visual básico) argumento, se debe lanzar System.ArgumentNullException lugar de su System.ArgumentException tipo base .

De hecho no se debe utilizar en absoluto ApplicationException (docs):

No derivan excepciones personalizadas de la T: Clase System.Exception en lugar de la T: Clase System.ApplicationException.

Originalmente se pensó que las excepciones personalizados deben derivar de la clase ApplicationException; sin embargo, esto no se ha encontrado para agregar un valor significativo. Para obtener más información, vea las Mejores prácticas para manejar excepciones.

InvalidOperationException está destinado no para cuando los argumentos a un método o propiedad no son válidos, pero para cuando la operación como un todo no es válido (docs). No se debe tirar del colocador:

Haz lanzar una excepción System.InvalidOperationException si en un estado inadecuado. System.InvalidOperationException debe lanzarse si un conjunto de propiedades o una llamada a un método no son apropiados dado el estado actual del objeto. Por ejemplo, escribir en un System.IO.FileStream que se haya abierto para leer debería arrojar una excepción System.InvalidOperationException.

Por cierto, InvalidOperationException es para cuando la operación no es válida para el estado actual del objeto. Si la operación es siempre válido para toda la clase, se debe utilizar NotSupportedException.

+0

@Joren, @Vadim indica que necesita un instructor predeterminado y no tiene un valor predeterminado razonable para este campo, por lo que simplemente monitorear al colocador no será suficiente para su escenario. – bdukes

+0

Tienes razón, voy a editar mi respuesta. – Joren

+0

@Joren, Excelente respuesta. – Vadim

1

¿El constructor lo establece en un valor no nulo? Si es así, simplemente arrojaría ArgumentNullException del colocador.

4

Yo arrojaría un InvalidOperationException. MSDN dice que "se lanza cuando una llamada a un método no es válida para el estado actual del objeto".

+1

No creo que sea apropiado para este escenario. InvalidOperationException solo debe utilizarse cuando el objeto invocado no es válido. En este caso, el objeto que se llama es 100% válido, es el argumento para el método que no es válido. – JaredPar

+2

Lanzaría 'InvalidOperationException' si el objeto estuviera en un estado tal que no fuera válido usar el setter de propiedades. Es decir, la * operación * en sí no es válida. Los documentos de MSDN dan el ejemplo de "escribir en un' System.IO.FileStream' que se ha abierto para leer ". – Joren

+0

Supongo que no conocemos el escenario exacto de lo que se llama. Estoy trabajando bajo la suposición de que estamos llamando a un método de una clase y tratando de usar una propiedad de esa clase dentro del método. En ese caso, el objeto no es válido para el método que se está llamando (ya que la propiedad debe establecerse antes de que se pueda llamar al método). Parece que @JaredPar está considerando el escenario en el que una propiedad de un argumento no se inicializa. En ese caso, su sugerencia de 'ArgumentException' tiene más sentido. – bdukes

2

Bueno, no es un argumento, si hace referencia a una propiedad de una clase. Por lo tanto, no debería usar ArgumentException o ArgumentNullException.

NullReferenceException sucedería si solo dejas las cosas en paz, así que supongo que eso no es lo que estás buscando.

Por lo tanto, usar ApplicationExeption o InvalidOperationException probablemente sea su mejor opción, asegurándose de dar una cadena significativa para describir el error.

+1

Estoy bastante seguro de que hay clases en .NET Framework que arrojan ArgumentException desde el setter de una propiedad. No recuerdo exactamente dónde, pero sí recuerdo haberlo encontrado. Podría argumentar que podría no ser "apropiado", pero independientemente del marco de referencia, ha establecido el precedente. – Tinister

2

Es muy apropiado lanzar ArgumentNullException si alguien intenta asignar null.

Una propiedad nunca debe arrojarse en una operación de lectura.

+0

¿De dónde sacas la idea de que una propiedad nunca debe arrojarse en una operación de lectura? Ciertamente puede suceder con bastante facilidad. –

+1

@John No creo que su punto fuera que no puede sino simplemente que no debería. –

+1

@John Fisher: Cwalina, Krzystof y Brad Abrams: "Pautas de diseño de Framework. Conventions, Idioms, and Patterns for Reutilizable .Net Libraries", Addison-Wesley, 2006. p. 121 - entre otras fuentes. –

1

Si el problema es que un miembro de un argumento, y no el argumento en sí mismo, es nulo, entonces creo que la mejor opción es la más genérica ArgumentException. ArgumentNullException no funciona aquí porque el argumento en realidad no es nulo. En su lugar, necesita el tipo de excepción más genérico "algo está mal con su argumento".

un mensaje detallado para el constructor sería muy apropiado en este caso

+0

¿Qué quiere decir "un mensaje de detalle para el constructor"? Debo tener un constructor predeterminado. – Vadim

+0

@Vadim una razón detallada acerca de por qué se lanza la excepción de argumento. Por ejemplo, "Propiedad Blah del parámetro foo debe ser no nulo" – JaredPar

+0

@Vadim, se refiere al constuctor de la excepción, no al constructor de su objeto. – bdukes

1

Si no puede ser nulo o vacío, haga que su colocador no permite valores nulos o vacíos, o lanzar una excepción ArgumentException si ese es el caso.

Además, requiere que la propiedad se establezca en el constructor.

De esta forma usted fuerza un valor válido, en lugar de volver más tarde y decir que no puede determinar el saldo de la cuenta porque la cuenta no está configurada.

Pero, estoy de acuerdo con la respuesta de bduke.

+1

Debo tener un constructor predeterminado. – Vadim

+0

No puede poner algún valor predeterminado válido ¿Lo tomo en el constructor? –

+1

Desafortunadamente no puedo jugar un juego de adivinanzas. No tengo idea de qué valor se debe establecer en tiempo de ejecución. – Vadim

1

Hay un precedente para extender la interpretación de ArgumentNullException al significado "argumento de cadena es nulo o vacío": System.Windows.Clipboard.SetText arrojará una ArgumentNullException en este caso.

Así que no vería nada malo con el uso de esto en lugar de la ArgumentException más general en su conjunto de propiedades, siempre que lo documente.

0

Acaba de lanzar lo que sea, siempre y cuando el mensaje de error es útil para un desarrollador. Esta clase de excepción nunca debería ocurrir fuera del desarrollo, de todos modos.

Cuestiones relacionadas