Meilleures pratiques en matière d'obtention, de définition et de propriétés. Java contre C #

92

Je suis actuellement en cours de C # et j'essaie de trouver la meilleure façon de faire les choses. Je viens d'un milieu Java et je ne connais donc que les meilleures pratiques Java; Je suis novice en C #!

En Java, si j'ai une propriété privée, je le fais;

private String name;

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

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

En C #, je vois qu'il existe de nombreuses façons de faire cela.

Je peux le faire comme Java:

private string name;

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

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

Ou je peux le faire de cette façon:

private string name;

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

Ou:

public string Name { get; set; }

Lequel devrais-je utiliser et quelles sont les mises en garde ou les subtilités impliquées dans chaque approche? Lors de la création de classes, je suis les meilleures pratiques générales que je connais de Java (en particulier la lecture de Effective Java). Ainsi par exemple, je privilégie l'immuabilité (fournir des setters uniquement lorsque cela est nécessaire). Je suis simplement curieux de voir comment ces pratiques s'intègrent dans les différentes manières de fournir des setters et des getters en C #; essentiellement, comment traduire les meilleures pratiques du monde Java en C #?

ÉDITER

J'étais en train de poster ceci en commentaire de la réponse de Jon Skeet, mais cela est devenu long:

Qu'en est-il d'une propriété non triviale (c'est-à-dire avec un traitement et une validation importants peut-être)? Pourrais-je toujours l'exposer via une propriété publique mais avec la logique encapsulée dans getet set? Pourquoi ferais-je / devrais-je faire cela au lieu d'avoir des méthodes setter et getter dédiées (avec la logique de traitement et de validation associée).

Vivin Paliath
la source

Réponses:

88

Pré-C # 6

J'utiliserais le dernier de ceux-ci, pour une propriété triviale. Notez que j'appellerais cela une propriété publique car les getters et les setters sont publics.

L'immuabilité est un peu pénible avec les propriétés implémentées automatiquement - vous ne pouvez pas écrire une propriété automatique qui n'a qu'un getter; le plus proche que vous pouvez venir est:

public string Foo { get; private set; }

ce qui n'est pas vraiment immuable ... juste immuable en dehors de votre classe. Vous souhaiterez peut-être utiliser une véritable propriété en lecture seule à la place:

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

Vous ne voulez certainement pas écrire getName()et setName(). Dans certains cas, il est judicieux d'écrire des méthodes Get / Set plutôt que d'utiliser des propriétés, en particulier si elles peuvent être coûteuses et que vous souhaitez le souligner. Cependant, vous voudriez suivre la convention de dénomination .NET de PascalCase pour les méthodes, et vous ne voudriez pas qu'une propriété triviale comme celle-ci soit implémentée avec des méthodes normales de toute façon - une propriété est beaucoup plus idiomatique ici.

C # 6

Hourra, nous avons enfin des propriétés implémentées automatiquement en lecture seule:

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

De même pour les propriétés en lecture seule qui font besoin de faire un peu de travail, vous pouvez utiliser les propriétés valides membres:

public double Area => height * width;
Jon Skeet
la source
5
Plus exact: toute révision de code indiquera que la méthode java est un hack qui contourne les constructions d'exécution de langauge AND (!) Valides et tue l'utilisation de la propriété en tant que proeprty (ie object.property = "value"). Dans certaines équipes, cela se traduirait par une belle discussion sur l'attitude - en fonction de l'ancienneté combinée à une incitation à mettre cette attitude à profit chez un concurrent. Sérieusement, NE COMBATTEZ PAS LA LANGAUGE. D'autant que la "voie" java est un hack qui a été choisi afin de ne pas modifier la langue du support immobilier.
TomTom
1
Je suppose que la bonne nouvelle est que ma réponse ne semble pas contredire tout ce que vous avez mentionné. La mauvaise nouvelle est que vos doigts sont beaucoup plus rapides que les miens. Excellente perspicacité et merci pour les détails supplémentaires.
jeremyalan
4
Pour répondre, modifiez: vous pouvez utiliser les méthodes get / set avec n'importe quelle quantité de logique que vous souhaitez. Nous faisons souvent cela, en particulier pour la validation. La meilleure pratique, cependant, est de ne pas avoir beaucoup de logique lente (accès à la base de données par exemple), de logique dangereuse (lancement d'exceptions) ou de mutation (changement de beaucoup d'état) dans les propriétés. Une propriété est censée agir plus ou moins comme un état simple. Tout autre élément doit être indiqué en utilisant une fonction à la place.
CodexArcanum
2
@rtindru: Oui, j'en suis conscient. Il est tout à fait possible d'écrire une propriété en lecture seule. Mais vous ne pouvez pas déclarer une propriété implémentée automatiquement sans un accesseur set.
Jon Skeet
2
À partir de C # 6, vous pouvez désormais avoir des propriétés automatiques sans accesseur défini , ( public int Y { get; } = y;).
Scott Chamberlain
17

Si vous n'avez besoin que d'une variable pour stocker certaines données:

public string Name { get; set; }

Vous voulez le faire apparaître en lecture seule?

public string Name { get; private set; }

Ou encore mieux ...

private readonly string _name;

...

public string Name { get { return _name; } }

Vous souhaitez vérifier la valeur avant d'attribuer la propriété?

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

      m_name = value;
   }
}

En général, GetXyz () et SetXyz () ne sont utilisés que dans certains cas, et vous n'avez qu'à utiliser votre instinct quand vous vous sentez bien. En général, je dirais que je m'attends à ce que la plupart des propriétés get / set ne contiennent pas beaucoup de logique et aient très peu, voire aucun effet secondaire inattendu. Si la lecture d'une valeur de propriété nécessite d'appeler un service ou d'obtenir l'entrée d'un utilisateur afin de créer l'objet que je demande, alors je l'envelopperais dans une méthode et l'appellerais quelque chose comme BuildXyz(), plutôt que GetXyz().

Jeremyalan
la source
2
Je ne jetterais pas d'exceptions dans les propriétés, je préfère utiliser des méthodes avec des contrats pour spécifier un tel comportement spécifique. Si une propriété est de type int, je m'attendrais à ce que chaque int soit qualifié. Quelque chose qui appelle à lever des exceptions n'est pas simple à mon avis, les invocations de type INotifyPropertyChanged sont plus dans cette ligne selon moi.
flindeberg
3
setter privé! = immuable
piedar
piedar a raison. Un setter privé signifie simplement que je ne peux pas faire d'affectations, mais que je peux toujours utiliser myList.Add(), par exemple (tant que l'objet est exposé au changement, il est mutable).
1
@flindeberg Désolé mais je ne suis pas d'accord ... Et si vous avez une barre de progression avec une valeur Min/ Max. Vous voulez vous en assurer Max > Min? Avoir un SetRange(Min, Max)peut avoir du sens, mais comment allez-vous relire ces valeurs? Propriétés Min / Max en lecture seule? Lancer des exceptions pour une entrée non valide semble être le moyen le plus propre de gérer cela.
base
12

Utilisez les propriétés en C #, pas les méthodes get / set. Ils sont là pour votre commodité et c'est idiomatique.

Quant à vos deux exemples C #, l'un est simplement du sucre syntaxique pour l'autre. Utilisez la propriété auto si tout ce dont vous avez besoin est un simple wrapper autour d'une variable d'instance, utilisez la version complète lorsque vous devez ajouter une logique dans le getter et / ou le setter.

Ed S.
la source
5

En C #, privilégiez les propriétés pour exposer les champs privés pour get et / ou set. Le formulaire que vous mentionnez est une propriété automatique où get et set génèrent automatiquement un champ de support de pivot caché pour vous.

Je préfère les propriétés automatiques lorsque cela est possible, mais vous ne devriez jamais faire une paire de méthodes set / get en C #.

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

Il s'agit simplement d'une propriété implémentée automatiquement et est techniquement la même qu'une propriété normale. Un champ de sauvegarde sera créé lors de la compilation.

Toutes les propriétés sont finalement converties en fonctions, de sorte que l'implémentation compilée réelle à la fin est la même que celle à laquelle vous êtes habitué en Java.

Utilisez les propriétés implémentées automatiquement lorsque vous n'avez pas à effectuer d'opérations spécifiques sur le champ de sauvegarde. Sinon, utilisez une propriété ordinaire. Utilisez les fonctions get et set lorsque l'opération a des effets secondaires ou est coûteuse en calcul, utilisez les propriétés autrement.

Steven Jeuris
la source
4

Quelle que soit la manière dont vous choisissez en C #, le résultat final est le même. Vous obtiendrez une variable de backinng avec des méthodes getter et setter séparées. En utilisant les propriétés, vous suivez les meilleures pratiques et c'est donc une question de niveau de détail que vous souhaitez obtenir.

Personnellement, je choisirais les propriétés automatiques, la dernière version:, public string Name { get; set; }car elles occupent le moins d'espace. Et vous pouvez toujours les développer à l'avenir si vous avez besoin d'ajouter quelque chose comme la validation.

Paul Sasik
la source
4

Dans la mesure du possible, je préfère le public string Name { get; set; }car il est concis et facilement lisible. Cependant, il peut y avoir des moments où celui-ci est nécessaire

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}
SquidScareMe
la source
2
pouvez-vous expliquer quand et pourquoi ce genre de chose est nécessaire?
Jesper
Pour le moment, je ne peux pas penser à une raison d'utiliser la 2ème version. J'ai seulement dit «peut-être des moments où cela est nécessaire». Je déteste utiliser des absolus sauf si je suis positif.
SquidScareMe
3
Et pourquoi est-ce «lolz»? Vous pouvez montrer votre soutien à un commentaire en votant pour.
SquidScareMe
1
Une des raisons d'utiliser un champ de sauvegarde explicite est lorsque vous souhaitez effectuer des opérations atomiques / thread-safe sur la valeur
Basic
4

En C #, la méthode préférée consiste à utiliser les propriétés plutôt que getX() et setX()méthodes. Notez également que C # ne nécessite pas que les propriétés aient à la fois un get et un set - vous pouvez avoir des propriétés get-only et des propriétés set-only.

public boolean MyProperty
{
    get { return something; }
}

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

Laissez-moi d'abord essayer d'expliquer ce que vous avez écrit:

// 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; }
}

Cette structure est également utilisée de nos jours, mais elle est plus appropriée si vous voulez faire des fonctionnalités supplémentaires, par exemple lorsqu'une valeur est définie, vous pouvez l'analyser pour la capitaliser et l'enregistrer dans un membre privé pour une utilisation interne.

Avec .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
}

Je vous souhaite une meilleure chance en C #

Waqas Raja
la source
3

Comme mentionné, toutes ces approches aboutissent au même résultat. Le plus important est que vous choisissiez une convention et que vous vous y teniez. Je préfère utiliser les deux derniers exemples de propriétés.

Jeff LaFay
la source
2

comme la plupart des réponses ici, utilisez les propriétés automatiques. Intuitif, moins de lignes de code et plus propre. Si vous devez sérialiser votre classe, marquez la classe [Serializable]/ avec l' [DataConract]attribut. Et si vous utilisez [DataContract]marquer le membre avec

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

Le setter privé ou public dépend de vos préférences.

Notez également que les propriétés automatiques nécessitent à la fois des getters et des setters (publics ou privés).

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

Sinon, si vous souhaitez uniquement obtenir / définir, créez vous-même un champ de sauvegarde

RAM
la source
0

Lequel devrais-je utiliser et quelles sont les mises en garde ou les subtilités impliquées dans chaque approche?

Lorsque vous utilisez des propriétés, il y a une mise en garde qui n'a pas encore été mentionnée: avec les propriétés, vous ne pouvez pas avoir de paramétrisation de vos getters ou setters.

Par exemple, imaginez que vous souhaitez récupérer des éléments de liste et que vous souhaitez également appliquer un filtre en même temps. Avec une méthode get, vous pouvez écrire quelque chose comme:

obj.getItems(filter);

En revanche, avec une propriété, vous êtes obligé de renvoyer d'abord tous les éléments

obj.items

puis appliquez le filtre à l'étape suivante ou vous devez ajouter des propriétés dédiées qui exposent des éléments filtrés par différents critères, ce qui gonfle bientôt votre API:

obj.itemsFilteredByX
obj.itemsFilteredByY

Ce qui peut parfois être une nuisance, c'est lorsque vous avez commencé avec une propriété, par exemple obj.items et que vous avez découvert plus tard qu'une paramétrisation getter ou setter est nécessaire ou faciliterait les choses pour l'utilisateur de l'API de classe. Vous devez maintenant soit réécrire votre API et modifier tous les endroits de votre code qui accèdent à cette propriété, soit trouver une solution alternative. En revanche, avec une méthode get, par exemple obj.getItems(), vous pouvez simplement étendre la signature de votre méthode pour accepter un objet "configuration" optionnel, par exemple obj.getItems(options)sans avoir à réécrire tous les endroits qui appellent votre méthode.

Cela étant dit, les propriétés (implémentées automatiquement) en C # sont toujours des raccourcis très utiles (pour les différentes raisons mentionnées ici) car la plupart du temps, le paramétrage n'est peut-être pas nécessaire - mais cette mise en garde demeure.

B12Toaster
la source