Si las propiedades tienen efectos secundarios

19

¿Deberían las propiedades en C # tener efectos secundarios además de notificar un cambio en sus estados?

He visto propiedades usadas de varias maneras diferentes. Desde las propiedades que cargarán el valor la primera vez que acceden a las propiedades que tienen efectos secundarios masivos como causar una redirección a una página diferente.

Irlanda
fuente
1
Entre los hallazgos de @ Job, ¿por qué debería evitar usar Propiedades en C #? La opinión de alias Jeffrey Richter es muy relevante para esta pregunta.
rwong
@rwong Relevante sí, pero completamente sin sentido; sin embargo, debe leerse (para estar al tanto de los problemas que destaca). Al menos en este asunto estoy en el campamento de Skeet.
Apoorv Khurasia
Aunque esto pertenece a los constructores, esto también es relevante: programmers.stackexchange.com/a/164255/1996
Jim G.

Respuestas:

40

Supongo que está hablando de propiedades de solo lectura , o al menos de captadores de propiedades , ya que un establecedor de propiedades tendrá , en casi todos los casos, efectos secundarios. De lo contrario, no es muy útil.

En general, un buen diseño sigue el principio de la menor sorpresa . No haga cosas que las personas que llaman no esperan que haga, especialmente si esas cosas pueden cambiar los resultados futuros .

En general , eso significa que los captadores de propiedades no deberían tener efectos secundarios.

Sin embargo , tengamos cuidado con lo que entendemos por "efecto secundario".

Un efecto secundario es, técnicamente, cualquier modificación de estado. Ese podría ser un estado de acceso público, o ... podría ser un estado totalmente privado.

Los cargadores perezosos / diferidos son un ejemplo de estado que es casi exclusivamente privado. Mientras no sea responsabilidad de quien llama liberar ese recurso, en realidad está reduciendo la sorpresa y la complejidad en general mediante el uso de inicialización diferida. Una persona que llama normalmente no espera tener que indicar explícitamente la inicialización de una estructura interna . Entonces, la inicialización perezosa no viola el principio anterior.

Otro ejemplo es una propiedad sincronizada. Para que un método sea seguro para subprocesos, a menudo tendrá que estar protegido por una sección crítica o mutex. Entrar en una sección crítica o adquirir un mutex es un efecto secundario; estás modificando el estado, generalmente el estado global . Sin embargo, este efecto secundario es necesario para evitar un tipo de sorpresa mucho peor : la sorpresa de que los datos sean modificados (o peor, parcialmente modificados) por otro hilo.

Así que aflojaría un poco la restricción a lo siguiente: las lecturas de propiedades no deberían tener efectos secundarios visibles o efectos secundarios que cambien su semántica .

Si no hay una forma posible de que una persona que llama se vea afectada por un efecto secundario , o que incluso lo note , ese efecto secundario no está causando ningún daño. Si fuera imposible para usted escribir una prueba para verificar la existencia de un efecto secundario en particular, entonces está lo suficientemente localizado como para etiquetarlo como un detalle de implementación privada y, por lo tanto, no es una preocupación legítima para el mundo exterior.

Pero ten cuidado; Como regla general, debe tratar de evitar los efectos secundarios, porque a menudo lo que usted cree que es un detalle de implementación privada puede filtrarse inesperadamente y hacerse público.

Aaronaught
fuente
2
1 - una buena respuesta completa incluyendo los casos más complicados que a su vez un "debe" en un "debe ... excepto cuando ...."
quickly_now
Otro ejemplo de un efecto secundario aceptable en un captador: una función de registro.
Loren Pechtel
5

Contestaré su pregunta con una pregunta: ¿Qué sucede cuando modifica la propiedad Ancho de un formulario?

Justo en la parte superior de mi cabeza, esto:

  • Cambiar el valor del campo de respaldo.
  • Dispara un evento.
  • Cambie las dimensiones del formulario, informe el cambio al administrador de ventanas y solicite una nueva pintura.
  • Notifique a los controles sobre la forma del cambio para que cualquiera de ellos que necesite cambiar el tamaño o la posición cuando se cambie el tamaño del formulario pueda hacerlo.
  • Causar todos los controles que cambiaron para disparar eventos y decirle al administrador de ventanas que necesitan ser redibujados.
  • Probablemente otras cosas también.

(Descargo de responsabilidad: soy un desarrollador de Delphi, no un desarrollador de .NET. Esto puede no ser 100% exacto para C #, pero apuesto a que está bastante cerca, especialmente teniendo en cuenta qué parte del marco .NET original se basó en Delphi).

Todos estos "efectos secundarios" ocurren cuando cambia la propiedad Ancho en un formulario. ¿Alguno de ellos parece inapropiado o incorrecto de alguna manera?

Mason Wheeler
fuente
siempre y cuando no entre en un bucle infinito que se llama a sí mismo. Para evitar esto, cada "cambio" se asignará al menos a tres eventos: uno disparado antes del cambio, otro disparado durante el cambio y otro disparado después de que haya terminado.
rwong
5

Eche un vistazo a las Reglas de diseño de Microsoft , especialmente una de ellas:

CA1024: use las propiedades donde corresponda

... Un método es un buen candidato para convertirse en una propiedad si una de estas condiciones está presente:

  • No toma argumentos y devuelve la información de estado de un objeto.
  • Acepta un solo argumento para establecer alguna parte del estado de un objeto.

Las propiedades deben comportarse como si fueran campos; Si el método no puede, no debe cambiarse a una propiedad. Los métodos son mejores que las propiedades en las siguientes situaciones:

  • El método realiza una operación que consume mucho tiempo. El método es perceptiblemente más lento que el tiempo requerido para establecer u obtener el valor de un campo.
  • El método realiza una conversión. Acceder a un campo no devuelve una versión convertida de los datos que almacena.
  • El método Get tiene un efecto secundario observable. Recuperar el valor de un campo no produce ningún efecto secundario.
  • El orden de ejecución es importante. Establecer el valor de un campo no depende de la ocurrencia de otras operaciones.
  • Llamar al método dos veces seguidas crea resultados diferentes.
  • El método es estático pero devuelve un objeto que la persona que llama puede cambiarlo. Recuperar el valor de un campo no permite que la persona que llama cambie los datos almacenados por el campo.
  • El método devuelve una matriz ...
Meditación Gurú
fuente
2

Creo que las propiedades no deberían tener efectos secundarios. Sin embargo, hay una excepción a esta regla: carga diferida. Si desea cargar algo de forma diferida en una propiedad, está bien, porque el cliente no puede decir que la carga diferida se está ejecutando, y generalmente no le importa.

EDITAR : Oh, una cosa más: disparar un evento que dice "¡PropertyXYZ ha cambiado!" (por ejemplo INotifyPropertyChanged) - está bien en los setters.

Billy ONeal
fuente
2

Además de la carga diferida mencionada anteriormente, también existe el caso de las funciones de registro. Estos serían raros pero no descartados.

Loren Pechtel
fuente
2

Casi no hay absolutos en la programación, para responder a su pregunta necesitamos analizar algunas propiedades deseables de los objetos y ver si eso es algo que se aplica a nuestro caso

  1. Queremos que las clases sean fácilmente comprobables.
  2. Queremos clases para apoyar la concurrencia
  3. Queremos que las clases sean fáciles de razonar para terceros

Si respondemos que sí a algunas de estas preguntas, entonces probablemente sea una buena idea pensar con mucho cuidado sobre las propiedades y sus efectos secundarios. Supongo que cuando dice efectos secundarios, se refiere a efectos secundarios secundarios, ya que actualizar el valor es un efecto secundario en sí mismo.

Cuantos más efectos secundarios en general, más difícil será evaluar una clase. Cuantos más efectos secundarios secundarios, más difícil será manejar la concurrencia, pero ese es un problema difícil que probablemente requiere un pensamiento de diseño importante de todos modos.

El gran problema es probablemente para las personas que tratan a su clase como una "caja negra", no estaría fácilmente disponible que algún estado ha cambiado solo porque han leído una propiedad o que alguna otra propiedad cambia porque otra cambia (lo que podría conducir a a actualizaciones en cascada).

Entonces, en general no, queremos que las propiedades sean tan fáciles de razonar y simples como sea posible, eso está un poco implícito en la decisión de diseño; de lo contrario, sería un método. Sin embargo, un buen programador siempre puede romper las reglas, solo asegúrese de tener una muy buena razón :)

Homde
fuente