2009-09-28 39 views
92

¿Cuándo es apropiado lanzar una excepción desde dentro de un elemento getter o setter de propiedades? ¿Cuándo no es apropiado? ¿Por qué? Los enlaces a documentos externos sobre el tema serían útiles ... Google apareció sorprendentemente poco.Mejores prácticas: arrojar excepciones desde las propiedades

+0

Ver también: http://stackoverflow.com/questions/633944/what-exception-to-throw-from-a-property-setter –

+0

También relacionado: http://stackoverflow.com/questions/ 1488322/what-exception-type-to-use-when-a-property-can-be-null/1488346 # 1488346 –

+1

Leí ambas preguntas, pero ninguna respondió a esta pregunta por completo. –

Respuesta

109

Microsoft tiene sus recomendaciones sobre cómo diseñar propiedades en http://msdn.microsoft.com/en-us/library/ms229006.aspx

Esencialmente, recomiendan que captadores de propiedad sean descriptores de acceso ligeros que son siempre seguros llamar. Recomiendan rediseñar getters para que sean métodos si las excepciones son algo que necesita lanzar. Para los instaladores, indican que las excepciones son una estrategia de manejo de errores apropiada y aceptable.

Para indizadores, Microsoft indica que es aceptable que tanto los getters como los setters arrojen excepciones. Y, de hecho, muchos indexadores de la biblioteca .NET hacen esto. La excepción más común es ArgumentOutOfRangeException.

Hay algunas muy buenas razones por las que no desea lanzar excepciones en captadores de propiedad:

  • Dado que las propiedades "parecen" ser campos, que no siempre es evidente que pueden lanzar un (por -diseño) excepción; mientras que con los métodos, los programadores están entrenados para esperar e investigar si las excepciones son una consecuencia esperada de invocar el método.
  • Los getters son utilizados por una gran cantidad de infraestructura .NET, como serializadores y enlaces de datos (en WinForms y WPF, por ejemplo), lidiar con excepciones en tales contextos puede convertirse rápidamente en un problema.
  • Los buscadores de propiedades son evaluados automáticamente por los depuradores cuando mira o inspecciona un objeto. Una excepción aquí puede ser confusa y ralentizar sus esfuerzos de depuración. Tampoco es deseable realizar otras operaciones costosas en propiedades (como acceder a una base de datos) por las mismas razones.
  • Las propiedades se utilizan a menudo en una convención de encadenamiento: obj.PropA.AnotherProp.YetAnother - con este tipo de sintaxis se vuelve problemático decidir dónde inyectar declaraciones de captura de excepción.

Como nota al margen, uno debe ser consciente de que sólo porque una propiedad se no diseñado para lanzar una excepción, eso no significa que no vaya; podría ser código de llamada que hace. Incluso el simple acto de asignar un nuevo objeto (como una cadena) podría dar como resultado excepciones. Siempre debe escribir su código de manera defensiva y esperar excepciones de todo lo que invoque.

+0

Esto no quiere decir que las excepciones nunca se lanzarán, sin embargo. Tenga en cuenta que cualquier propiedad Obtener que asigna JIT tiene el potencial de arrojar debido al constructor. ¿Realmente el código sería mejor si detectara esa excepción en silencio? –

+37

Si se encuentra con una excepción fatal como "falta de memoria", no importa si obtiene la excepción en una propiedad o en otro lugar. Si no lo conseguiste en la propiedad, lo obtendrías un par de nanosegundos después de lo siguiente que asigna la memoria. La pregunta no es "¿puede una propiedad lanzar una excepción?" Casi todos los códigos pueden lanzar una excepción debido a una condición fatal. La pregunta es si una propiedad debe * diseñar * lanzar una excepción como parte de su contrato específico. –

+1

No estoy seguro de entender el argumento en esta respuesta. Por ejemplo, con respecto al enlace de datos: tanto WinForms como WPF están escritos específicamente para manejar adecuadamente las excepciones lanzadas por las propiedades y tratarlas como fallas de validación, lo cual es una forma perfectamente perfecta (algunos creen que es la mejor) para proporcionar validación del modelo de dominio . –

22

Casi nunca es apropiado para un captador, y algunas veces es apropiado para un colocador.

El mejor recurso para este tipo de preguntas es "Framework Design Guidelines" por Cwalina y Abrams; está disponible como un libro encuadernado, y grandes porciones de él también están disponibles en línea.

Desde el punto 5.2: Diseño Propiedad

evitar tirar excepciones a captadores de propiedad. Los captadores de propiedades deben ser operaciones simples y no deben tener condiciones previas.Si un getter puede lanzar una excepción, debería ser rediseñado como método . Tenga en cuenta que esta regla no se aplica a los indexadores , donde esperamos excepciones como resultado de validar los argumentos.

Tenga en cuenta que esta directiva solo aplica a los captores de propiedades. Está bien lanzar una excepción en un conjunto de propiedades.

+2

Si bien (en general) estoy de acuerdo con estas pautas, creo que es útil proporcionar alguna información adicional sobre por qué se deben seguir, y qué tipo de consecuencias pueden surgir cuando se ignoran. – LBushkin

+3

¿Cómo se relaciona esto con los objetos desechables y la guía que debe considerar arrojando 'ObjectDisposedException' una vez que el objeto ha tenido' Dispose() 'llamado y algo posteriormente solicita un valor de propiedad? Parece que la guía debe ser "evitar arrojar excepciones de captadores de propiedades, a menos que el objeto haya sido eliminado, en cuyo caso debería considerar lanzar una Excepción de objeto suspendido". –

+4

El diseño es el arte y la ciencia de encontrar compromisos razonables frente a requisitos conflictivos. De cualquier manera parece un compromiso razonable; No me sorprendería tener un lanzamiento de objeto dispuesto en una propiedad; ni me sorprendería si no fuera así. Como usar un objeto eliminado es una terrible práctica de programación, no sería prudente tener expectativas. –

1

Todo esto está documentado en MSDN (como vinculados en otras respuestas), pero aquí hay una regla general ...

En el organismo, si su propiedad debe ser validada por encima y más allá del tipo. Por ejemplo, una propiedad llamada PhoneNumber probablemente debería tener validación de expresiones regulares y debería arrojar un error si el formato no es válido.

Para getters, posiblemente cuando el valor es nulo, pero lo más probable es que sea algo que querrá manejar en el código de llamada (según las pautas de diseño).

31

No hay nada de malo en lanzar excepciones de los instaladores. Después de todo, ¿qué mejor manera de indicar que el valor no es válido para una propiedad determinada?

Para los getters, en general no se respeta, y eso se puede explicar con bastante facilidad: un getter de propiedad, en general, informa el estado actual de un objeto; por lo tanto, el único caso donde es razonable que un getter arroje es cuando el estado es inválido. Pero también se considera generalmente que es una buena idea diseñar sus clases de forma que simplemente no sea posible obtener un objeto no válido inicialmente, o ponerlo en estado inválido por medios normales (es decir, siempre asegurar la inicialización completa en los constructores, y intente hacer que los métodos sean a prueba de excepciones con respecto a la validez del estado y las invariantes de clase). Siempre y cuando te apegues a esa regla, tus compradores de propiedades nunca deben entrar en una situación en la que tengan que informar un estado no válido, y por lo tanto nunca lanzar.

Existe una excepción que conozco, y en realidad es bastante importante: cualquier objeto que implemente IDisposable. Dispose está diseñado específicamente como una forma de llevar el objeto a un estado no válido, e incluso hay una clase de excepción especial, ObjectDisposedException, que se utilizará en ese caso. Es perfectamente normal arrojar ObjectDisposedException de cualquier miembro de la clase, incluidos los captadores de propiedades (y excluyendo el Dispose mismo), después de que el objeto haya sido eliminado.

+3

Gracias Pavel. Esta respuesta entra en "por qué" en lugar de simplemente decir nuevamente que no es una buena idea arrojar una excepción de las propiedades. – SolutionYogi

+1

No me gusta la noción de que absolutamente todos los miembros de un 'IDisposable' deberían volverse inútiles después de un' Dispose'. Si invocar a un miembro requeriría el uso de un recurso que 'Dispose' ha dejado de estar disponible (por ejemplo, el miembro leería datos de una secuencia que se ha cerrado), el miembro debería arrojar' ObjectDisposedException' en lugar de filtrar, p. 'ArgumentException', pero si uno tiene un formulario con propiedades que representan los valores en ciertos campos, parece mucho más útil permitir que dichas propiedades se lean después de su eliminación (produciendo los últimos valores tipeados) que requerir ... – supercat

+1

. ..que 'Dispose' se difiera hasta que se hayan leído todas esas propiedades. En algunos casos donde un hilo puede usar lecturas de bloqueo en un objeto mientras otro lo cierra, y donde los datos pueden llegar en cualquier momento antes de 'Dispose', puede ser útil tener' Dispose' para cortar los datos entrantes, pero permitir previamente- datos recibidos para ser leídos Uno no debería forzar una distinción artificial entre 'Cerrar' y 'Disponer' en situaciones en las que de otro modo no habría necesidad de existir. – supercat

1

Un acercamiento agradable a excepciones es usarlos para documentar código por sí mismo y otros desarrolladores de la siguiente manera:

Las excepciones deben ser para los estados de programas excepcionales. ¡Esto significa que está bien escribirlos donde quieras!

Una razón por la que puede querer ponerlos en getters es documentar la API de una clase: si el software arroja una excepción tan pronto como un programador intenta usarla incorrectamente, ¡entonces no la usarán mal! Por ejemplo, si tiene validación durante un proceso de lectura de datos, puede no ser lógico poder continuar y acceder a los resultados del proceso si hubo errores fatales en los datos. En este caso, es posible que desee obtener el lanzamiento de salida si hubo errores para garantizar que otro programador compruebe esta condición.

Son una forma de documentar las suposiciones y los límites de un subsistema/método/lo que sea. ¡En el caso general, no deberían ser atrapados!Esto también se debe a que nunca se lanzan si el sistema está trabajando de la manera esperada: si se produce una excepción, muestra que las suposiciones de un fragmento de código no se cumplen; por ejemplo, no interactúa con el mundo que lo rodea en el camino Originalmente estaba destinado a. Si detecta una excepción que fue escrita para este propósito, probablemente signifique que el sistema ha entrado en un estado impredecible/inconsistente; esto puede conducir a una falla o corrupción de datos o similar que probablemente sea mucho más difícil de detectar/depurar.

Los mensajes de excepción son una forma muy grosera de informar errores: no se pueden recopilar en masa y solo contienen realmente una cadena. Esto los hace inadecuados para informar problemas en los datos de entrada. En funcionamiento normal, el sistema no debería ingresar un estado de error. Como resultado de esto, los mensajes en ellos deben diseñarse para programadores y no para usuarios: las cosas que son incorrectas en los datos de entrada se pueden descubrir y transmitir a los usuarios en formatos más adecuados (personalizados).

La excepción (jaja!) A esta regla es cosas como IO donde las excepciones no están bajo su control y no se pueden verificar con anticipación.

+2

¿Cómo se invalidó esta respuesta válida y relevante? No debe haber política en StackOverflow, y si esta respuesta parece pasar por alto el bullseye, agregue un comentario en ese sentido. Bajar el voto es para respuestas que son irrelevantes o incorrectas. – debater

0

Esta es una pregunta y respuesta muy compleja, depende de cómo se use el objeto. Como regla general, los captadores de propiedades y los intermediarios que son "vinculantes tardíos" no deben arrojar excepciones, mientras que las propiedades con "vinculación anticipada" exclusiva deben arrojar excepciones cuando sea necesario. Por cierto, la herramienta de análisis de código de Microsoft está definiendo el uso de propiedades demasiado estrictamente, en mi opinión.

"enlace tardío" significa que las propiedades se encuentran a través de la reflexión. Por ejemplo, el atributo Serializables se utiliza para serializar/deserializar un objeto a través de sus propiedades. Lanzar una excepción durante este tipo de situación rompe las cosas de manera catastrófica y no es una buena forma de usar excepciones para crear un código más sólido.

"vinculación anticipada" significa que un uso de la propiedad está vinculado en el código por el compilador. Por ejemplo, cuando un código que escribe hace referencia a un filtro de propiedades. En este caso, está bien arrojar excepciones cuando tienen sentido

Un objeto con atributos internos tiene un estado determinado por los valores de esos atributos. Las propiedades que expresan atributos que son conscientes y sensibles al estado interno del objeto no deben usarse para el enlace tardío. Por ejemplo, supongamos que tiene un objeto que debe ser abierto, accedido, luego cerrado. En este caso, el acceso a las propiedades sin abrir primero debe dar como resultado una excepción. Supongamos, en este caso, que no lanzamos una excepción y permitimos que el código acceda a un valor sin lanzar una excepción. El código parecerá feliz a pesar de que obtuvo un valor de un getter que no tiene sentido. Ahora hemos puesto el código que llamó al getter en una mala situación ya que debe saber cómo verificar el valor para ver si no tiene sentido. Esto significa que el código debe hacer suposiciones sobre el valor que obtuvo del captador de propiedades para validarlo. Así es como se escribe mal el código.

0

Tenía este código en el que no estaba seguro de qué excepción arrojar.

public Person 
{ 
    public string Name { get; set; } 
    public boolean HasPets { get; set; } 
} 

public void Foo(Person person) 
{ 
    if (person.Name == null) { 
     throw new Exception("Name of person is null."); 
     // I was unsure of which exception to throw here. 
    } 

    Console.WriteLine("Name is: " + person.Name); 
} 

Anticipéme el modelo de tener la propiedad de ser nula en el primer lugar forzando como un argumento en el constructor.

public Person 
{ 
    public Person(string name) 
    { 
     if (name == null) { 
      throw new ArgumentNullException(nameof(name)); 
     } 
     Name = name; 
    } 

    public string Name { get; private set; } 
    public boolean HasPets { get; set; } 
} 

public void Foo(Person person) 
{ 
    Console.WriteLine("Name is: " + person.Name); 
} 
Cuestiones relacionadas