En su excelente libro, CLR Via C #, Jeffrey Richter dijo que no le gustan las propiedades y recomienda no usarlas. Dio alguna razón, pero realmente no entiendo. ¿Alguien puede explicarme por qué debería o no debería utilizar propiedades? En C # 3.0, con propiedades automáticas, ¿esto cambia?
Como referencia agregué las opiniones de Jeffrey Richter:
• Una propiedad puede ser de solo lectura o de solo escritura; el acceso al campo es siempre legible y escribible. Si define una propiedad, es mejor ofrecer métodos de acceso get y set.
• Un método de propiedad puede generar una excepción; el acceso al campo nunca lanza una excepción.
• Una propiedad no se puede pasar como parámetro de salida o referencia a un método; un campo puede. Por ejemplo, el siguiente código no se compilará:
using System;
public sealed class SomeType
{
private static String Name
{
get { return null; }
set {}
}
static void MethodWithOutParam(out String n) { n = null; }
public static void Main()
{
// For the line of code below, the C# compiler emits the following:
// error CS0206: A property or indexer may not
// be passed as an out or ref parameter
MethodWithOutParam(out Name);
}
}
• Un método de propiedad puede tardar mucho en ejecutarse; el acceso al campo siempre se completa inmediatamente. Una razón común para usar propiedades es realizar la sincronización de subprocesos, que puede detener el subproceso para siempre y, por lo tanto, no se debe utilizar una propiedad si se requiere sincronización de subprocesos. En esa situación, se prefiere un método. Además, si se puede acceder a su clase de forma remota (por ejemplo, su clase se deriva de System.MashalByRefObject), llamar al método de propiedad será muy lento y, por lo tanto, se prefiere un método a una propiedad. En mi opinión, las clases derivadas de MarshalByRefObject nunca deberían usar propiedades.
• Si se llama varias veces seguidas, un método de propiedad puede devolver un valor diferente cada vez; un campo devuelve el mismo valor cada vez. La clase System.DateTime tiene una propiedad Now de solo lectura que devuelve la fecha y hora actuales. Cada vez que consulte esta propiedad, devolverá un valor diferente. Esto es un error, y Microsoft desea poder arreglar la clase haciendo de Now un método en lugar de una propiedad.
• Un método de propiedad puede causar efectos secundarios observables; el acceso al campo nunca lo hace. En otras palabras, un usuario de un tipo debería poder establecer varias propiedades definidas por un tipo en cualquier orden que elija sin notar ningún comportamiento diferente en el tipo.
• Un método de propiedad puede requerir memoria adicional o devolver una referencia a algo que en realidad no es parte del estado del objeto, por lo que modificar el objeto devuelto no tiene ningún efecto sobre el objeto original; consultar un campo siempre devuelve una referencia a un objeto que está garantizado como parte del estado del objeto original. Trabajar con una propiedad que devuelve una copia puede resultar muy confuso para los desarrolladores y esta característica no suele estar documentada.
fuente
Respuestas:
La razón de Jeff para no gustarle las propiedades es que parecen campos, por lo que los desarrolladores que no entienden la diferencia los tratarán como si fueran campos, asumiendo que serán baratos de ejecutar, etc.
Personalmente, no estoy de acuerdo con él en este punto en particular: creo que las propiedades hacen que el código del cliente sea mucho más simple de leer que las llamadas al método equivalente. Estoy de acuerdo en que los desarrolladores necesitan saber que las propiedades son básicamente métodos disfrazados, pero creo que educar a los desarrolladores sobre eso es mejor que hacer que el código sea más difícil de leer usando métodos. (En particular, habiendo visto el código Java con varios getters y setters que se llaman en la misma declaración, sé que el código C # equivalente sería mucho más simple de leer. La Ley de Demeter está muy bien en teoría, pero a veces
foo.Name.Length
realmente lo es lo correcto para usar ...)(Y no, las propiedades implementadas automáticamente no cambian nada de esto).
Esto se parece un poco a los argumentos en contra del uso de métodos de extensión: puedo entender el razonamiento, pero el beneficio práctico (cuando se usa con moderación) supera la desventaja en mi opinión.
fuente
Bueno, tomemos sus argumentos uno por uno:
Esto es una ventaja para las propiedades, ya que tiene un control de acceso más detallado.
Si bien esto es cierto en su mayor parte, puede llamar a un método en un campo de objeto no inicializado y generar una excepción.
Justa.
También puede llevar muy poco tiempo.
No es verdad. ¿Cómo sabe que el valor del campo no ha cambiado (posiblemente por otro hilo)?
Si es un error, es menor.
Justa.
La mayoría de las protestas también se podrían decir de los getters y setters de Java, y las tuvimos durante bastante tiempo sin tales problemas en la práctica.
Creo que la mayoría de los problemas se podrían resolver con un mejor resaltado de sintaxis (es decir, diferenciando las propiedades de los campos) para que el programador sepa qué esperar.
fuente
_field
. O incluso en minúsculasfield
.No he leído el libro y no has citado la parte que no entiendes, así que tendré que adivinar.
A algunas personas no les gustan las propiedades porque pueden hacer que su código haga cosas sorprendentes.
Si escribo
Foo.Bar
, las personas que lo lean normalmente esperarán que simplemente acceda a un campo miembro de la clase Foo. Es una operación barata, casi gratuita y determinista. Puedo repetirlo una y otra vez y obtener el mismo resultado cada vez.En cambio, con propiedades, en realidad podría ser una llamada a función. Podría ser un bucle infinito. Podría abrir una conexión a la base de datos. Puede devolver valores diferentes cada vez que accedo a él.
Es un argumento similar a por qué Linus odia C ++. Su código puede resultar sorprendente para el lector. Odia la sobrecarga del operador:
a + b
no significa necesariamente una simple adición. Puede significar una operación enormemente complicada, como las propiedades de C #. Puede tener efectos secundarios. Puede hacer cualquier cosa.Honestamente, creo que este es un argumento débil. Ambos idiomas están llenos de cosas como esta. (¿Deberíamos evitar la sobrecarga de operadores en C # también? Después de todo, se puede usar el mismo argumento allí)
Las propiedades permiten la abstracción. Podemos fingir que algo es un campo normal y usarlo como si lo fuera, y no tener que preocuparnos por lo que sucede detrás de escena.
Eso generalmente se considera algo bueno, pero obviamente se basa en que el programador escriba abstracciones significativas. Tus propiedades deberían comportarse como campos. No deberían tener efectos secundarios, no deberían realizar operaciones costosas o inseguras. Queremos poder pensar en ellos como campos.
Sin embargo, tengo otra razón para encontrarlos menos que perfectos. No se pueden pasar por referencia a otras funciones.
Los campos se pueden pasar como
ref
, lo que permite que una función llamada acceda a ellos directamente. Las funciones se pueden pasar como delegados, lo que permite que una función llamada acceda a ella directamente.Propiedades ... no puedo.
Eso apesta.
Pero eso no significa que las propiedades sean malas o no deban usarse. Para muchos propósitos, son geniales.
fuente
ref
parámetros; un miembro (por ejemploFoo
) de un formulariovoid Foo<T>(ActionByRef<Point,T> proc, ref T param)
con un atributo especial, y hacer que el compilador se transformething.Foo.X+=someVar;
enFoo((ref Point it, ref int param)=>it.X += param, ref someVar);
. Dado que lambda es un delegado estático, no se requeriría ningún cierre y el usuario de un objeto podría usar cualquier ubicación de almacenamiento que respalde la propiedad como unref
parámetro genuino (y podría pasarla a otros métodos comoref
parámetro).En 2009, este consejo simplemente parecía una queja de la variedad Who Moved My Cheese . Hoy en día, es casi ridículamente obsoleto.
Un punto muy importante que muchas respuestas parecen andar de puntillas, pero que no abordan de frente es que estos supuestos "peligros" de las propiedades son una parte intencional del diseño del marco.
Sí, las propiedades pueden:
Especifique diferentes modificadores de acceso para el captador y el definidor. Esta es una ventaja sobre los campos. Un patrón común es tener un captador público y un definidor interno o protegido , una técnica de herencia muy útil que no se puede lograr solo con campos.
Lanza una excepción. Hasta la fecha, este sigue siendo uno de los métodos de validación más efectivos, especialmente cuando se trabaja con marcos de interfaz de usuario que involucran conceptos de enlace de datos. Es mucho más difícil asegurarse de que un objeto permanezca en un estado válido cuando se trabaja con campos.
Tarda mucho en ejecutarse. La comparación válida aquí es con métodos , que toman el mismo tiempo, no con campos . No se da ninguna base para la afirmación "se prefiere un método" que no sea la preferencia personal de un autor.
Devuelve valores diferentes de su getter en ejecuciones posteriores. Esto casi parece una broma en una proximidad tan cercana al punto de ensalzar las virtudes de los parámetros
ref
/out
con campos, cuyo valor de un campo después de una llamadaref
/out
está prácticamente garantizado que será diferente de su valor anterior, y de manera impredecible.Si estamos hablando del caso específico (y prácticamente académico) del acceso de un solo subproceso sin acoplamientos aferentes, se comprende bastante bien que es solo un mal diseño de propiedad el tener efectos secundarios de cambio de estado visible, y tal vez mi memoria es se desvanece, pero parece que no puedo recordar ningún ejemplo de personas que usan
DateTime.Now
y esperan que salga el mismo valor cada vez. Al menos no en ningún caso en el que no lo hubieran estropeado tanto con una hipótesisDateTime.Now()
.Causar efectos secundarios observables, que es, por supuesto, precisamente la razón por la que las propiedades se inventaron en primer lugar como una característica del lenguaje. Las propias pautas de diseño de propiedades de Microsoft indican que el orden de los establecedores no debería importar, ya que hacerlo de otra manera implicaría un acoplamiento temporal . Ciertamente, no puede lograr el acoplamiento temporal con campos solo, pero eso es solo porque no puede hacer que ocurra ningún comportamiento significativo con campos solo, hasta que se ejecute algún método.
Los descriptores de acceso a la propiedad pueden ayudar a prevenir ciertos tipos de acoplamiento temporal al forzar el objeto a un estado válido antes de que se lleve a cabo cualquier acción; por ejemplo, si una clase tiene un
StartDate
y unEndDate
, entonces establecer elEndDate
antes delStartDate
podría forzar elStartDate
retroceso también. Esto es cierto incluso en entornos asíncronos o de subprocesos múltiples, incluido el ejemplo obvio de una interfaz de usuario basada en eventos.Otras cosas que pueden hacer las propiedades y que los campos no pueden incluir:
Type
oName
derivadas puede proporcionar metadatos interesantes pero constantes sobre sí mismos.Item(i)
llamadas reconocerán como algo maravilloso.Richter es claramente un autor prolífico y sabe mucho sobre CLR y C #, pero tengo que decir que parece que cuando escribió originalmente este consejo (no estoy seguro de si está en sus revisiones más recientes, espero sinceramente que no) que simplemente no quería dejar atrás los viejos hábitos y estaba teniendo problemas para aceptar las convenciones de C # (vs. C ++, por ejemplo).
Lo que quiero decir con esto es que su argumento de "propiedades consideradas dañinas" esencialmente se reduce a una sola declaración: las propiedades parecen campos, pero pueden no actuar como campos. Y el problema con la afirmación es que no es verdad o, en el mejor de los casos, es muy engañoso. Las propiedades no parecen campos; al menos, se supone que no deben verse como campos.
Hay dos convenciones de codificación muy fuertes en C # con convenciones similares compartidas por otros lenguajes CLR, y FXCop le gritará si no las sigue:
Por lo tanto, no hay ambigüedad sobre si
Foo.Bar = 42
es un descriptor de acceso de propiedad o un descriptor de acceso de campo. Es un descriptor de acceso de propiedad y debe tratarse como cualquier otro método: puede ser lento, puede generar una excepción, etc. Esa es la naturaleza de la abstracción : depende totalmente de la discreción de la clase declarante cómo reaccionar. Los diseñadores de clases deben aplicar el principio de la mínima sorpresa, pero las personas que llaman no deben asumir nada sobre una propiedad, excepto que hace lo que dice en la lata. Eso es a propósito.La alternativa a las propiedades son los métodos getter / setter en todas partes. Ese es el enfoque de Java y ha sido controvertido desde el principio . Está bien si ese es tu bolso, pero no es así como nos movemos en el campo de .NET. Intentamos, al menos dentro de los límites de un sistema de tipo estático, evitar lo que Fowler llama ruido sintáctico . No queremos paréntesis adicionales, adicionales
get
/set
verrugas, o firmas de métodos adicionales - No si podemos evitarlos sin pérdida de claridad.Di lo que quieras, pero
foo.Bar.Baz = quux.Answers[42]
siempre será mucho más fácil de leer quefoo.getBar().setBaz(quux.getAnswers().getItem(42))
. Y cuando lee miles de líneas de esto al día, marca la diferencia.(Y si su respuesta natural al párrafo anterior es decir, "seguro que es difícil de leer, pero sería más fácil si lo dividiera en varias líneas", entonces lamento decir que no entendió por completo .)
fuente
No veo ninguna razón por la que no debería usar Propiedades en general.
Las propiedades automáticas en C # 3+ solo simplifican un poco la sintaxis (al estilo del azúcar sintético).
fuente
Es solo la opinión de una persona. He leído bastantes libros de C # y todavía no he visto a nadie que diga "no usar propiedades".
Personalmente, creo que las propiedades son una de las mejores cosas de c #. Le permiten exponer el estado a través de cualquier mecanismo que desee. Puede crear una instancia perezosamente la primera vez que se usa algo y puede hacer la validación al establecer un valor, etc. Al usarlas y escribirlas, pienso en las propiedades como establecedores y captadores que tienen una sintaxis mucho mejor.
En cuanto a las advertencias con las propiedades, hay un par. Uno es probablemente un mal uso de propiedades, el otro puede ser sutil.
En primer lugar, las propiedades son tipos de métodos. Puede resultar sorprendente si coloca lógica complicada en una propiedad porque la mayoría de los usuarios de una clase esperan que la propiedad sea bastante ligera.
P.ej
Encuentro que usar métodos para estos casos ayuda a hacer una distinción.
En segundo lugar, y lo que es más importante, las propiedades pueden tener efectos secundarios si las evalúa durante la depuración.
Digamos que tiene una propiedad como esta:
Ahora suponga que tiene una excepción que ocurre cuando se realizan demasiadas consultas. ¿Adivina qué sucede cuando comienzas a depurar y traspasar la propiedad en el depurador? Cosas malas. ¡Evite hacer esto! Mirar la propiedad cambia el estado del programa.
Estas son las únicas salvedades que tengo. Creo que los beneficios de las propiedades superan con creces los problemas.
fuente
Esa razón debe haberse dado en un contexto muy específico. Por lo general, es al revés: se recomienda usar propiedades, ya que le brindan un nivel de abstracción que le permite cambiar el comportamiento de una clase sin afectar a sus clientes ...
fuente
No puedo evitar elegir los detalles de las opiniones de Jeffrey Richter:
Incorrecto: los campos se pueden marcar como de solo lectura, por lo que solo el constructor del objeto puede escribir en ellos.
Incorrecto: la implementación de una clase puede cambiar el modificador de acceso de un campo de público a privado. Los intentos de leer campos privados en tiempo de ejecución siempre darán como resultado una excepción.
fuente
No estoy de acuerdo con Jeffrey Richter, pero puedo adivinar por qué no le gustan las propiedades (no he leído su libro).
Aunque las propiedades son como métodos (en cuanto a implementación), como usuario de una clase, espero que sus propiedades se comporten "más o menos" como un campo público, por ejemplo:
Desafortunadamente, he visto propiedades que no se comportaron de esa manera. Pero el problema no son las propiedades en sí mismas, sino las personas que las implementaron. Entonces solo requiere algo de educación.
fuente
Hay un momento en el que considero no usar propiedades, y eso es al escribir el código .Net Compact Framework. El compilador CF JIT no realiza la misma optimización que el compilador JIT de escritorio y no optimiza los accesos de propiedades simples, por lo que en este caso agregar una propiedad simple provoca una pequeña cantidad de código sobre el uso de un campo público. Por lo general, esto no sería un problema, pero casi siempre en el mundo de Compact Framework se enfrenta a limitaciones de memoria estrictas, por lo que incluso pequeños ahorros como este cuentan.
fuente
No debe evitar usarlos, pero debe usarlos con calificación y cuidado, por las razones dadas por otros colaboradores.
Una vez vi una propiedad llamada algo así como Clientes que internamente abrían una llamada fuera de proceso a una base de datos y leían la lista de clientes. El código del cliente tenía un 'for (int i to Customers.Count)' que estaba provocando una llamada separada a la base de datos en cada iteración y para el acceso del Cliente seleccionado. Ese es un ejemplo atroz que demuestra el principio de mantener la propiedad muy liviana, rara vez más que un acceso de campo interno.
Un argumento para usar propiedades es que le permiten validar el valor que se establece. Otra es que el valor de la propiedad puede ser un valor derivado, no un solo campo, como TotalValue = cantidad * cantidad.
fuente
Personalmente, solo uso propiedades cuando creo métodos simples de obtención / configuración. Me desvío de él cuando llego a estructuras de datos complicadas.
fuente
El argumento asume que las propiedades son malas porque parecen campos, pero pueden realizar acciones sorprendentes. Esta suposición queda invalidada por las expectativas de los programadores de .NET:
Las propiedades no parecen campos. Los campos parecen propiedades.
Por lo tanto, un campo es como una propiedad que está garantizada para no generar nunca una excepción.
Entonces, un campo es como una propiedad, pero tiene capacidades adicionales: pasar a métodos
ref
/out
accept.Entonces, un campo es como una propiedad rápida.
Por lo tanto, un campo es como una propiedad que está garantizada para devolver el mismo valor a menos que el campo se establezca en un valor diferente.
Nuevamente, un campo es una propiedad que está garantizada para no hacer eso.
Este puede ser sorprendente, pero no porque sea una propiedad que lo haga. Más bien, es que casi nadie devuelve copias mutables en las propiedades, por lo que el caso del 0.1% es sorprendente.
fuente
La invocación de métodos en lugar de propiedades reduce en gran medida la legibilidad del código de invocación. En J #, por ejemplo, usar ADO.NET era una pesadilla porque Java no admite propiedades e indexadores (que son esencialmente propiedades con argumentos). El código resultante era extremadamente feo, con llamadas a métodos de paréntesis vacías por todas partes.
El soporte para propiedades e indexadores es una de las ventajas básicas de C # sobre Java.
fuente