Mejores prácticas de getters, setters y propiedades. Java frente a C #

92

Estoy tomando una clase de C # en este momento y estoy tratando de encontrar la mejor manera de hacer las cosas. Vengo de una experiencia en Java, por lo que solo estoy familiarizado con las mejores prácticas de Java; ¡Soy un novato en C #!

En Java, si tengo una propiedad privada, hago esto;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

En C #, veo que hay muchas formas de hacer esto.

Puedo hacerlo como Java:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

O puedo hacerlo de esta manera:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

O:

public string Name { get; set; }

¿Cuál debería usar y cuáles son las advertencias o sutilezas involucradas con cada enfoque? Al crear clases, sigo las mejores prácticas generales que conozco de Java (especialmente leyendo Effective Java). Entonces, por ejemplo, estoy favoreciendo la inmutabilidad (proporcionando setters solo cuando sea necesario). Solo tengo curiosidad por ver cómo encajan estas prácticas con las diversas formas de proporcionar setters y getters en C #; esencialmente, ¿cómo traduciría las mejores prácticas del mundo de Java a C #?

EDITAR

Estaba publicando esto como un comentario a la respuesta de Jon Skeet, pero luego se hizo largo:

¿Qué pasa con una propiedad no trivial (es decir, con un procesamiento y validación significativos tal vez)? ¿Podría todavía exponerlo a través de una propiedad pública pero con la lógica encapsulada en gety set? ¿Por qué debería / debería hacer esto en lugar de tener métodos de establecimiento y obtención dedicados (con procesamiento asociado y lógica de validación)?

Vivin Paliath
fuente

Respuestas:

88

Pre-C # 6

Usaría el último de estos, por una propiedad trivial. Tenga en cuenta que llamaría a esto público propiedad ya que tanto los captadores como los definidores son públicos.

La inmutabilidad es un poco complicada con propiedades implementadas automáticamente: no se puede escribir una propiedad automática que solo tenga un captador; lo más cerca que puedes venir es:

public string Foo { get; private set; }

que no es realmente inmutable ... simplemente inmutable fuera de tu clase. Por lo tanto, es posible que desee utilizar una propiedad real de solo lectura en su lugar:

private readonly string foo;
public string Foo { get { return foo; } }

Definitivamente no quieres escribir getName()y setName(). En algunos casos, tiene sentido escribir métodos Get / Set en lugar de usar propiedades, especialmente si pueden ser costosos y desea enfatizar eso. Sin embargo, querrá seguir la convención de nomenclatura de .NET de PascalCase para los métodos, y no querrá que una propiedad trivial como esta se implemente con métodos normales de todos modos; una propiedad es mucho más idiomática aquí.

C # 6

Hurra, finalmente tenemos las propiedades adecuadas implementadas automáticamente de solo lectura:

// This can only be assigned to within the constructor
public string Foo { get; }

Lo mismo sucede con propiedades de sólo lectura, que hacen necesidad de hacer algún trabajo, puede utilizar las propiedades de miembro de cuerpo:

public double Area => height * width;
Jon Skeet
fuente
5
Más exacto: cualquier revisión de código señalará que Java es un truco que está pasando por alto el lenguaje válido Y (!) Construcciones en tiempo de ejecución y elimina el uso de la propiedad como propiedad (es decir, object.property = "valor"). En algunos equipos, esto resultaría en una buena charla sobre la actitud, dependiendo de la antigüedad combinada con un incentivo para poner esa actitud en práctica en un competidor. En serio, NO LUCHA CONTRA EL LENGUAJE. Especialmente porque la "forma" de Java es un truco que se eligió para no modificar el idioma para el soporte de bienes raíces.
TomTom
1
Supongo que la buena noticia es que mi respuesta no parece contradecir nada de lo que mencionaste. La mala noticia es que tus dedos son mucho más rápidos que los míos. Gran conocimiento y gracias por el detalle añadido.
jeremyalan
4
Para responderle, edite: puede usar los métodos get / set con cualquier cantidad de lógica que desee. A menudo hacemos esto, especialmente para la validación. Sin embargo, la mejor práctica es no tener mucha lógica lenta (acceso a la base de datos, por ejemplo), lógica peligrosa (lanzamiento de excepciones) o mutación (cambiar mucho de estado) en las propiedades. Se espera que una propiedad actúe más o menos como un estado simple. Cualquier otra cosa debe indicarse utilizando una función en su lugar.
CodexArcanum
2
@rtindru: Sí, soy consciente de eso. Es completamente posible escribir una propiedad de solo lectura. Pero no puede declarar una propiedad implementada automáticamente sin un descriptor de acceso establecido.
Jon Skeet
2
A partir de C # 6 , ahora puede tener propiedades automáticas sin un descriptor de acceso establecido , ( public int Y { get; } = y;).
Scott Chamberlain
17

Si todo lo que necesita es una variable para almacenar algunos datos:

public string Name { get; set; }

¿Quieres que parezca de solo lectura?

public string Name { get; private set; }

O mejor...

private readonly string _name;

...

public string Name { get { return _name; } }

¿Quiere comprobar el valor antes de asignar la propiedad?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

En general, GetXyz () y SetXyz () solo se usan en ciertos casos, y solo tiene que usar su instinto cuando se sienta bien. En general, diría que espero que la mayoría de las propiedades get / set no contengan mucha lógica y tengan muy pocos efectos secundarios inesperados, si es que tienen alguno. Si leer el valor de una propiedad requiere invocar un servicio o obtener información de un usuario para construir el objeto que estoy solicitando, entonces lo envolvería en un método y lo llamaría algo como BuildXyz(), en lugar de GetXyz().

jeremyalan
fuente
2
No lanzaría excepciones en las propiedades, prefiero usar establecedores de métodos con contratos para especificar ese comportamiento específico. Si una propiedad es de tipo int, esperaría que todos los int califiquen. Algo que requiere el lanzamiento de excepciones no es simple en mi opinión, las invocaciones de tipo INotifyPropertyChanged están más en esa línea, según yo.
flindeberg
3
setter privado! = inmutable
piedar
piedar tiene razón. Un establecedor privado simplemente significa que no puedo hacer asignaciones, pero aún puedo usar myList.Add(), por ejemplo (siempre que el objeto esté expuesto a cambios, es mutable).
1
@flindeberg Lo siento, pero no estoy de acuerdo ... ¿Qué pasa si tienes una barra de progreso con un valor Min/ Max? ¿Quiere asegurarse de eso Max > Min? Tener un SetRange(Min, Max)podría tener sentido, pero entonces, ¿cómo vas a leer esos valores? ¿Propiedades mínimas / máximas de solo lectura? Lanzar excepciones para entradas no válidas parece ser la forma más limpia de manejar esto.
Básico
12

Use propiedades en C #, no métodos get / set. Están ahí para su conveniencia y es idiomático.

En cuanto a sus dos ejemplos de C #, uno es simplemente azúcar sintáctico para el otro. Use la propiedad auto si todo lo que necesita es un contenedor simple alrededor de una variable de instancia, use la versión completa cuando necesite agregar lógica en el getter y / o setter.

Ed S.
fuente
5

En C # favorece las propiedades para exponer campos privados para get y / o set. La forma que mencionas es una propiedad automática en la que get y set generan automáticamente un campo de respaldo de pivote oculto para ti.

Prefiero las propiedades automáticas cuando sea posible, pero nunca debe hacer un par de métodos set / get en C #.

James Michael Hare
fuente
5
public string Name { get; set; }

Esta es simplemente una propiedad implementada automáticamente y técnicamente es lo mismo que una propiedad normal. Se creará un campo de respaldo al compilar.

Todas las propiedades se convierten eventualmente en funciones, por lo que la implementación compilada real al final es la misma a la que está acostumbrado en Java.

Utilice propiedades implementadas automáticamente cuando no tenga que realizar operaciones específicas en el campo de respaldo. Utilice una propiedad ordinaria de lo contrario. Use las funciones get y set cuando la operación tenga efectos secundarios o sea computacionalmente costosa, use propiedades de lo contrario.

Steven Jeuris
fuente
4

Independientemente de la forma que elija en C #, el resultado final es el mismo. Obtendrá una variable backinng con métodos getter y setter separados. Al usar propiedades, está siguiendo las mejores prácticas y, por lo tanto, es una cuestión de cuán detallado desea obtener.

Personalmente elegiría auto-propiedades, la última versión:, public string Name { get; set; }ya que ocupan la menor cantidad de espacio. Y siempre puede expandirlos en el futuro si necesita agregar algo como validación.

Paul Sasik
fuente
4

Siempre que sea posible, prefiero el público, string Name { get; set; }ya que es conciso y fácil de leer. Sin embargo, puede haber ocasiones en las que este sea necesario

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}
CalamarScareMe
fuente
2
¿Puede explicar cuándo y por qué es necesario ese tipo de cosas?
Jesper
Ahora mismo no puedo pensar en una razón para usar la segunda versión. Solo dije 'puede haber momentos en que esto sea necesario'. Odio usar absolutos a menos que sea positivo.
SquidScareMe
3
¿Y por qué es esto 'lolz'? Puede mostrar su apoyo a un comentario votando a favor.
SquidScareMe
1
Una razón para usar un campo de respaldo explícito es cuando desea realizar operaciones atómicas / seguras para subprocesos contra el valor
Básico
4

En C #, la forma preferida es a través de propiedades en lugar de métodos getX()y setX(). Además, tenga en cuenta que C # no requiere que las propiedades tengan tanto un get como un set; puede tener propiedades de solo get y propiedades de solo set.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}
Zach Johnson
fuente
4

Primero déjame intentar explicarte lo que escribiste:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

Esta estructura también se usa hoy en día, pero es más adecuada si desea hacer alguna funcionalidad adicional, por ejemplo, cuando se establece un valor, puede analizarlo para capitalizarlo y guardarlo en un miembro privado para modificar el uso interno.

Con .net framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

Te deseo mejor suerte en C #

Waqas Raja
fuente
3

Como se mencionó, todos estos enfoques dan como resultado el mismo resultado. Lo más importante es que elija una convención y se ciña a ella. Prefiero usar los dos últimos ejemplos de propiedades.

Jeff LaFay
fuente
2

como la mayoría de las respuestas aquí, use propiedades automáticas. Intuitivo, menos líneas de código y más limpio. Si debe serializar su clase, marque la clase [Serializable]/ con el [DataConract]atributo. Y si está utilizando, [DataContract]marque el miembro con

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

El setter privado o público depende de su preferencia.

También tenga en cuenta que las propiedades automáticas requieren captadores y definidores (públicos o privados).

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

Alternativamente, si solo desea obtener / configurar, cree un campo de respaldo usted mismo

RAM
fuente
0

¿Cuál debería usar y cuáles son las advertencias o sutilezas involucradas en cada enfoque?

Cuando se trata de propiedades, hay una advertencia que aún no se ha mencionado: con propiedades no puede tener ninguna parametrización de sus getters o setters.

Por ejemplo, imagine que desea recuperar los elementos de una lista y también desea aplicar un filtro al mismo tiempo. Con un método de obtención, podría escribir algo como:

obj.getItems(filter);

Por el contrario, con una propiedad está obligado a devolver primero todos los artículos

obj.items

y luego aplique el filtro en el siguiente paso o tiene que agregar propiedades dedicadas que exponen elementos filtrados por diferentes criterios, lo que pronto hincha su API:

obj.itemsFilteredByX
obj.itemsFilteredByY

Lo que a veces puede ser una molestia es cuando comenzó con una propiedad, por ejemplo, obj.itemsy luego descubrió que se necesita una parametrización de getter o setter o que facilitaría las cosas para el usuario de la API de clase. Ahora necesitaría reescribir su API y modificar todos esos lugares en su código que acceden a esta propiedad o encontrar una solución alternativa. Por el contrario, con un método get, por ejemplo obj.getItems(), puede simplemente extender la firma de su método para aceptar un objeto de "configuración" opcional, por ejemploobj.getItems(options) sin tener que reescribir todos esos lugares que llaman a su método.

Dicho esto, las propiedades (implementadas automáticamente) en C # siguen siendo atajos muy útiles (por las diversas razones mencionadas aquí) ya que la mayoría de las veces la parametrización puede no ser necesaria, pero esta advertencia se mantiene.

B12 Tostadora
fuente