¿Lanzar una excepción de una propiedad es una mala forma?

15

Siempre he pensado que las propiedades (es decir, sus operaciones set / get) deberían ser rápidas / inmediatas y sin fallas. Nunca debería tener que tratar de atrapar y obtener o establecer una propiedad.

Pero estoy buscando algunas formas de aplicar seguridad basada en roles en las propiedades de algunos objetos. Por ejemplo, una propiedad Employee.Salary. Algunas de las soluciones que he encontrado que otras han probado (una en particular es el ejemplo de AOP aquí ) implican lanzar una excepción si el descriptor de acceso no tiene los permisos correctos, pero esto va en contra de una regla personal que he tenido por mucho tiempo ahora.

Entonces pregunto: ¿estoy equivocado? ¿Han cambiado las cosas? ¿Se ha aceptado que las propiedades deberían poder lanzar excepciones?

Steven Evers
fuente
1
Esta misma pregunta en StackOverflow recibió más atención y mejores respuestas.
Roman Starkov

Respuestas:

14

cuando establece el valor de una propiedad, lanzar una excepción en un valor no válido está bien

obtener el valor de una propiedad (casi) nunca debería arrojar una excepción

para el acceso basado en roles, use interfaces o fachadas diferentes / más tontas; ¡no dejes que la gente vea cosas que no puede tener!

Steven A. Lowe
fuente
5

Considero que en su mayoría es una mala forma, pero incluso Microsoft recomienda usar excepciones a veces. Un buen ejemplo es la propiedad abstracta Stream.Length . A medida que avanzan las pautas, me preocuparía más evitar los efectos secundarios en los captadores y limitar los efectos secundarios en los setters.

ChaosPandion
fuente
4

Definitivamente, argumentaría que hay un defecto en el diseño si siente la necesidad de lanzar excepciones de un establecedor de propiedades o getter.

Una propiedad es una abstracción que representa algo que es solo un valor . Y debe poder establecer un valor sin temor a que hacerlo pueda generar una excepción. *

Si establecer la propiedad resulta en un efecto secundario, eso debería implementarse realmente como un método. Y si no produce ningún efecto secundario, no se debe lanzar ninguna excepción.

Un ejemplo ya mencionado en una respuesta diferente es la Stream.Positionpropiedad. Esto produce efectos secundarios y puede generar excepciones. Pero este configurador de propiedades es básicamente un envoltorioStream.Seek que podría llamar en su lugar.

Personalmente, creo que el puesto no debería haber sido una propiedad escribible.

Otro ejemplo en el que podría verse tentado a lanzar una excepción desde un configurador de propiedades es en la validación de datos:

public class User {
    public string Email {
        get { return _email; }
        set { 
            if (!IsValidEmail(value)) throw InvalidEmailException(value);
            _email = value;
        }
    }

Pero hay una mejor solución a este problema. Introduzca un tipo que represente una dirección de correo electrónico válida:

public class Email {
    public Email(string value) {
        if (!IsValidEmail(value)) throw new InvalidEmailException(value);
        ...
    }
    ...
}

public class User {
    public Email Email { get; set; }
}

los Email clase garantiza que no puede contener un valor que no sea una dirección de correo electrónico válida, y las clases que necesitan almacenar correos electrónicos quedan libres del deber de validarlas.

Esto también conduce a una mayor cohesión (un indicador de un buen diseño de software): el conocimiento sobre qué es una dirección de correo electrónico y cómo se valida solo existe en el Email clase, que solo tiene esa preocupación.

* ObjectDisposedException es la única excepción válida (sin juego de palabras) que se me ocurre en este momento.

Pete
fuente
2

Sé que su pregunta es específica de .NET , pero dado que C # comparte algo de historia con Java, pensé que podría estar interesado. Soy no de ninguna manera implica que, debido a que se haga algo en Java, se debe hacer en C #. Sé que los dos son muy diferentes, especialmente en cómo C # tiene un soporte de nivel de lenguaje muy mejorado para las propiedades. Solo estoy dando un poco de contexto y perspectiva.

De la especificación JavaBeans :

Propiedades restringidas A veces, cuando se produce un cambio de propiedad, es posible que algún otro bean desee validar el cambio y rechazarlo si es inapropiado. Nos referimos a las propiedades que se someten a este tipo de verificación como propiedades restringidas. En Java Beans, se requieren métodos de establecimiento de propiedades restringidos para admitir PropertyVetoException. Esto documenta a los usuarios de la propiedad restringida que las actualizaciones intentadas pueden ser vetadas. Entonces, una propiedad restringida simple podría verse así:

PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

Toma todo esto con un grano de sal, por favor. La especificación JavaBeans es antigua, y las propiedades de C # son (IMO) una gran mejora con respecto a las propiedades basadas en "convención de nomenclatura" que tiene Java. Solo estoy tratando de dar un poco de contexto, ¡eso es todo!

Mike Clark
fuente
Es un buen punto, pero la distinción entre un método setter y un set de propiedades ac # es lo suficientemente importante como para ser un caso diferente para mí. Del mismo modo, las excepciones comprobadas de Java obligan al código de llamada a ser consciente de que se puede lanzar una excepción, por lo que hay menos posibilidades de que esto sorprenda a cualquiera.
Steven Evers
2

El punto de una propiedad es el Principio de acceso uniforme , es decir, que un valor debe ser accesible a través de la misma interfaz, ya sea implementado por almacenamiento o cálculo. Si su propiedad está lanzando una excepción que representa una condición de error más allá del control del programador, del tipo que se supone que debe ser capturado y manejado, está obligando a su cliente a saber que el valor se obtiene a través del cálculo.

Por otro lado, no veo ningún problema con el uso de afirmaciones o excepciones de aserción que pretenden indicar el uso incorrecto de la API al programador en lugar de ser detectado y manejado. En estos casos, la respuesta correcta desde la perspectiva del usuario de la API no es manejar la excepción (por lo tanto, preocuparse implícitamente sobre si el valor se obtiene a través del cálculo o el almacenamiento). Es para arreglar su código para que el objeto no termine en un estado no válido o hacer que corrija su código para que la afirmación no se active.

dsimcha
fuente
No veo cómo "estás obligando a tu cliente a saber que el valor se obtiene a través del cálculo" que sigue. Ciertamente, el acceso al almacenamiento también puede generar una excepción. De hecho, ¿cómo definirías la diferencia entre los dos?
Timwi
Creo que la distinción que se hace es que simplemente recuperar un valor de un campo (almacenamiento) y hacer algo inocuo con él, como colocarlo en una variable de un tipo apropiado (es decir, sin conversión), no puede arrojar una excepción. En ese caso, una excepción solo podría ocurrir si posteriormente hiciste algo con el valor. Si se lanza una excepción en un captador de propiedades, entonces en ese caso simplemente el acto de recuperar el valor y colocarlo en una variable podría causar una excepción.
Aprendiz del Dr. Wily
1

AFAIK, esta guía proviene principalmente del proceso de pensamiento de que las propiedades pueden terminar siendo utilizadas en el momento del diseño . (Por ejemplo, la propiedad Text en un TextBox) Si la propiedad arroja una excepción cuando un diseñador intenta acceder a ella, VS no tendrá un buen día. Obtendrá un montón de errores al intentar usar el diseñador y para los diseñadores de la interfaz de usuario, no se procesarán. También se aplica al tiempo de depuración, aunque lo que verá en un escenario de excepción es solo "xxx Exception" y no afectará VS IIRC.

Para los POCO, en realidad no hará ningún daño, pero aún así evito hacerlo yo mismo. Me imagino que las personas querrán acceder a las propiedades con mayor frecuencia, por lo que normalmente deberían ser de bajo costo. Las propiedades no deberían hacer el trabajo de los métodos, solo deberían obtener / configurar cierta información y hacerlo como una regla general.

desaparecido en combate
fuente
0

Aunque mucho dependerá del contexto, generalmente evitaría poner cualquier tipo de lógica rompible (incluidas las excepciones) en las propiedades. Solo como un ejemplo, hay muchas cosas que usted (o alguna biblioteca que está usando) podría hacer que usan la reflexión para iterar sobre las propiedades que resultan en errores que no anticipó en tiempo de diseño.

Sin embargo, digo en general, ya que puede haber ocasiones en las que, definitivamente, quieras bloquear algo sin preocuparte demasiado por las consecuencias. Seguridad, supongo, sería el caso clásico allí.

FinnNk
fuente