comprendre les setters privés


90

Je ne comprends pas la nécessité d'avoir des setters privés qui ont commencé avec C # 2.

Avoir une méthode de définition pour moi permet à l'utilisateur de définir certaines variables dans cette classe. Ce faisant, nous n'exposerons pas les variables directement aux utilisateurs. Au lieu de cela, nous les laissons faire via cette méthode de définition publique.

Ceci pour moi utilise "l'encapsulation". Certains arguments prétendent que les setters privés vous permettront d'appliquer l'encapsulation.

Est-ce que je n'utilise pas l'encapsulation en utilisant des méthodes de définition publiques? Pourquoi avons-nous besoin de setters privés?

Quelle est la différence entre une classe immuable et une classe avec des setters privés?


1
J'ai beaucoup aimé les setters privés - m'a aidé à repenser les classes laides. Ils font également impossible de déclarer et définir une variable d'instance non constante à la fois comme ceci: private File settingsFile = null;puis dans l' un des constructeurs: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Refactoriser du code comme ça me faisait parfois pleurer :). Ce n'est pas parce que vous pouvez définir un membre avant le constructeur que vous devriez, car, avec plusieurs constructeurs, cela rend DIFFICILE de suivre la logique. Les setters privés vous obligent à définir des valeurs à l'intérieur du constructeur ou ultérieurement.
Hamish Grubijan

Réponses:


261

Logiquement.

La présence d'un setter privé est due au fait que vous pouvez utiliser la propriété automatique:

public int MyProperty { get; set; }

Que feriez-vous si vous souhaitez le rendre en lecture seule?

public int MyProperty { get; }

Oh merde !! Je ne peux pas y accéder depuis ma propre classe; Je devrais le créer comme une propriété normale:

private int myProperty;
public int MyProperty { get { return myProperty; } }

Hmm ... mais j'ai perdu la fonction "Propriété automatique" ...

public int MyProperty { get; private set; }

AHHH .. c'est mieux !!


Merci. Cela a du sens à nouveau
Dene

3
@ktutnik Merci d'avoir présenté ceci comme vous l'avez fait. Cela a du sens pour moi aussi, maintenant!
Vivek M. Chawla

3
Réponse parfaitement illustrée.
imnk

3
Oh crap!! I can't access it from my own classÀ partir de C # 6.0, cela n'est vrai qu'en dehors de la phase d'initialisation. Voir ma réponse stackoverflow.com/a/34223746/198797
tsemer

1
Ajouter à la réponse de # tsemer, avec c # 6, {get; }n'est PAS équivalent à { get; private set; }. Pour la première manière property.GetSetMethod(true)revient nullet la seconde true. Cela m'a surpris.
emragins

37

Un setter privé est utile si vous avez une propriété en lecture seule et que vous ne souhaitez pas déclarer explicitement la variable de sauvegarde.

Alors:

public int MyProperty
{
    get; private set;
}

est le même que:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

Pour les propriétés non implémentées automatiquement, cela vous donne un moyen cohérent de définir la propriété de l' intérieur votre classe afin que si vous avez besoin de validation, etc., vous ne l'ayez qu'à un seul endroit.

Pour répondre à votre dernière question, le MSDN a ceci à dire sur les setters privés:

Cependant, pour les petites classes ou structures qui encapsulent juste un ensemble de valeurs (données) et ont peu ou pas de comportements, il est recommandé de rendre les objets immuables en déclarant l'accesseur set comme privé.

À partir de la page MSDN sur les propriétés implémentées automatiquement


1
Je suis désolé de ne pas voir la valeur ajoutée du fait d'avoir des setters privés. Si nous ne voulons pas exposer le setter, nous n'avons qu'un getter. Si nous voulons ajouter un validateur, nous pouvons avoir un setter public et y ajouter une validation. Pourquoi avons-nous besoin d'un setter qui n'est pas accessible? La façon dont je comprends les choses est comme "Prends cette voiture mais tu ne peux pas la conduire" pourquoi veux-tu me donner la voiture si je ne vais pas la conduire de toute façon
Dene

@Dene - Vous pouvez certainement le faire, car ce n'est pas faux. La mise en œuvre automatique des propriétés n'est pas obligatoire.
ChrisF

Je sais qu'il n'y a rien de mal à faire comme je l'ai exprimé. C'est juste pour moi d'apprécier l'amélioration faite par C # 2. Il semble y avoir beaucoup de battage médiatique autour de lui mais je ne peux tout simplement pas le sentir ou voir la valeur.
Dene

@Dene, j'ai raté tout le battage médiatique à ce sujet. Mais, quand j'ai finalement vu qu'il était possible de faire cela, j'étais heureux parce que je savais comment nettoyer certaines longues classes de l'ère .Net 1.1. Bien que vous puissiez probablement utiliser la valeur de la propriété qui n'a pas encore été définie, cela est moins naturel que d'utiliser la valeur d'une variable membre d'instance qui a été définie sur null.
Hamish Grubijan

Cela simplifie le code. Tout comme les propriétés automobiles. La plupart des mises à jour ultérieures de C # visent à rendre le code plus laconique et donc lisible. Vous pouvez toujours faire les choses dans l'autre sens si vous le souhaitez - la rétrocompatibilité semble également être un grand objectif. Je suppose que c'est une question de goût.
niico

18

C'est assez simple. Les setters privés vous permettent de créer des propriétés publiques ou protégées en lecture seule.

C'est tout. C'est la seule raison.

Oui, vous pouvez créer une propriété en lecture seule en spécifiant uniquement le getter, mais avec les propriétés implémentées automatiquement, vous devez spécifier à la fois get et set, donc si vous voulez qu'une propriété implémentée automatiquement soit en lecture seule, vous devez utiliser setters privés. Il n'y a pas d'autre moyen de le faire.

Il est vrai que les setters privés n'ont pas été créés spécifiquement pour les propriétés en lecture seule implémentées automatiquement, mais leur utilisation est un peu plus ésotérique pour d'autres raisons, principalement centrées sur les propriétés en lecture seule et l'utilisation de la réflexion et de la sérialisation.


2
Merci "si vous voulez qu'une propriété implémentée automatiquement soit en lecture seule, vous devez utiliser des setters privés". Cela a du sens pour moi
Dene

17

Avec l'introduction de C # 6.0 et de la syntaxe des initialiseurs de propriétés automatiques , les setters privés ne sont plus nécessaires pour les propriétés définies uniquement lors de l'initialisation, en ligne ou dans le constructeur.

Ces nouvelles syntaxes compilent désormais:

Propriété initialisée en ligne

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

Propriété initialisée par le constructeur

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}

3
@Ziggler, en effet, ce n'est pas le cas. OP ne le demande pas non plus. Il ne comprend tout simplement pas la nécessité de les avoir. Cela répond: "vous n'avez plus besoin de les avoir dans ce scénario".
tsemer

6

Je ne comprends pas la nécessité d'avoir des setters privés qui ont commencé avec C # 2.

Par exemple, la classe de facture permet à l'utilisateur d'ajouter ou de supprimer des éléments de la propriété Items mais elle ne permet pas à l'utilisateur de modifier la référence Items (c'est-à-dire que l'utilisateur ne peut pas affecter la propriété Items à une autre instance d'objet de liste d'articles).


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}

+1 pour souligner que les propriétés peuvent être manipulées via le getter public, même si elles ont un setter privé.
user1725145

4

Disons, par exemple, que vous ne stockez pas la variable réelle via la propriété ou n'utilisez pas la valeur pour calculer quelque chose.

Dans ce cas, vous pouvez soit créer une méthode pour faire votre calcul

private void Calculate(int value)
{
 //...
}

Ou vous pouvez le faire en utilisant

public int MyProperty {get; private set;}

Dans ces cas, je recommanderais d'utiliser le plus tard, car les propriétés refactorisent chaque élément membre intact.

Autre que cela, si même disons que vous mappez la propriété avec une variable. Dans un tel cas, dans votre code, vous voulez écrire comme ceci:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

Le code ci-dessus semble horrible car le programmeur doit toujours faire attention à utiliser MyProperty pour Get et myprop pour Set.

Rether pour la cohérence, vous pouvez utiliser un setter privé qui rend la propriété en lecture seule à l'extérieur tandis que vous pouvez utiliser son setter à l'intérieur de votre code.


3

Je pense que quelques personnes ont dansé autour de cela, mais pour moi, la valeur des setters privés est que vous pouvez encapsuler le comportement d'une propriété, même au sein d'une classe. Comme l'a noté abhishek, si vous souhaitez déclencher un événement de modification de propriété à chaque fois qu'une propriété change, mais que vous ne voulez pas qu'une propriété soit en lecture / écriture dans le public, vous devez soit utiliser un setter privé, soit déclencher le événement partout où vous modifiez le champ de sauvegarde. Ce dernier est sujet aux erreurs car vous pourriez oublier. De même, si la mise à jour d'une valeur de propriété entraîne l'exécution d'un calcul ou la modification d'un autre champ, ou une initialisation paresseuse de quelque chose, vous voudrez également l'envelopper dans le setter privé plutôt que de devoir vous rappeler de le faire partout où vous faites utilisation du champ de support.


2

L'encapsulation signifie que l'état d'un objet se produit uniquement via une interface définie, et de ce fait, la classe peut s'assurer que cet état est toujours valide et conforme à l'objectif de la classe.

Dans certains cas, il est donc parfaitement conforme au principe de l'encapsulation de simplement exposer un champ publiquement - toutes les valeurs possibles du champ sont valides avec toutes les autres valeurs possibles de tous les autres champs, et par conséquent le programmeur peut activement décider d'autoriser le champ être manipulé librement par un code extérieur.

Ces cas sont cependant pour la plupart limités aux classes qui sont pour la plupart des "données anciennes simples". Ils ne sont pas non plus très intéressants à cet égard, donc assez à leur sujet.

Dans d'autres cas, dans d'autres langages, on aurait une méthode getter et setter, quelque chose comme int getId()pour obtenir une valeur et la void setId(int val)mettre à jour.

Les propriétés nous permettent d'utiliser la même syntaxe pour la lecture et l'écriture via des méthodes que nous utiliserions pour lire et écrire un champ. C'est un bon sucre syntaxique, mais pas essentiel.

(En fait, en raison de la façon dont la réflexion fonctionne et des cas tels que DataBinder.Evalcela peut être pratique d'avoir une propriété même lorsqu'un champ fonctionnerait bien, mais c'est une autre question).

Jusqu'à l'introduction des setters privés (en fait, ce qui a changé avec C # 2 est la syntaxe pour avoir un setter privé et un getter public ou protégé dans le même bloc), nous pourrions avoir une méthode privée pour faire le travail du setter privé, donc les setters privés ne sont pas vraiment nécessaires. Ils sont pratiques cependant, alors qu'ils ne sont que du sucre syntaxique, ils sont assez utiles.

L'encapsulation n'est pas une question de savoir si vos setters (ou getters) sont publics, privés, protégés ou internes, mais une question de savoir s'ils sont appropriés . Commencez par une valeur par défaut de chaque champ étant privé (et d'ailleurs readonly), puis, si nécessaire, ajoutez des membres (qu'il s'agisse de propriétés ou de méthodes) qui modifient ces champs et assurez-vous que l'objet reste valide à mesure qu'ils changent . Cela garantit que l' invariant d' une classe est conservé, ce qui signifie que les règles décrivant l'ensemble d'états valide dans lequel il peut être ne sont jamais brisées (les constructeurs aident également en s'assurant qu'il démarre dans un état valide).

Quant à votre dernière question, être immuable signifie qu'une classe n'a pas de setters publics, protégés ou internes et aucune méthode publique, protégée ou interne qui modifie les champs. Il y a des degrés de ceci, en C # il y a trois degrés possibles:

  1. Tous les champs d'instance d'une classe le sont readonly, donc même le code privé ne peut pas le modifier. Il est garanti qu'il est immuable (tout ce qui tente de le changer ne sera pas compilé) et des optimisations peuvent éventuellement être effectuées à l'arrière de cela.

  2. Une classe est immuable de l'extérieur car aucun membre public ne change quoi que ce soit, mais il n'est pas garanti par l'utilisation de readonlyqu'elle ne soit pas modifiée de l'intérieur.

  3. Une classe est immuable vue de l'extérieur, bien qu'un état soit un changement en tant que détail d'implémentation. Par exemple, un champ pourrait être mémorisé, et par conséquent, tandis que de l'extérieur une tentative pour l'obtenir récupère simplement la même valeur, la première tentative de ce type le calcule en fait et le stocke pour la récupération lors des tentatives suivantes.


1

Vous avez besoin d'un setter privé, si vous souhaitez prendre en charge le scénario suivant (non seulement pour cela, mais cela devrait indiquer une bonne raison): Vous avez une propriété en lecture seule dans votre classe, c'est-à-dire que seule la classe elle-même est autorisée à changer il, mais il peut le changer après la construction de l'instance. Pour les liaisons, vous devrez alors déclencher un événement PropertyChanged, de préférence cela devrait être fait dans le setter de propriété (privé). En fait, vous pouvez simplement déclencher l'événement PropertyChanged depuis un autre endroit de la classe, mais utiliser le setter privé pour cela est une "bonne citoyenneté", car vous ne distribuez pas vos déclencheurs de changement de propriété dans toute votre classe, mais gardez-le au propriété, où il appartient.


1

Oui, vous utilisez l'encapsulation en utilisant des propriétés, mais l'encapsulation comporte plus de nuances que la simple prise de contrôle sur la façon dont les propriétés sont lues et écrites. Le refus de définir une propriété depuis l'extérieur de la classe peut être utile à la fois pour la robustesse et les performances.

Une classe immuable est une classe qui ne change pas une fois qu'elle est créée, donc des setters privés (ou aucun setters du tout) sont nécessaires pour protéger les propriétés.

Les setters privés sont devenus plus fréquents avec le raccourci de propriété qui a été introduit en C # 3. En C # 2, le setter était souvent simplement omis et les données privées accédaient directement une fois définies.

Cette propriété:

public int Size { get; private set; }

est le même que:

private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

sauf que le nom de la variable de sauvegarde est créé en interne par le compilateur, vous ne pouvez donc pas y accéder directement.

Avec la propriété abrégée, le setter privé est nécessaire pour créer une propriété en lecture seule, car vous ne pouvez pas accéder directement à la variable de sauvegarde.


Si je me souviens bien, vous ne pourriez pas avoir de modificateurs d'accès différents sur getet setavant C # 2.0. De plus, je pense que vous mélangez 2.0 et 3.0, car le raccourci auto-implémenté auquel vous faites référence était 3.0.
Anthony Pegram

1

Je ne comprends pas la nécessité d'avoir des setters privés qui ont commencé avec C # 2.

Exemple de cas d'utilisation:

J'ai une instance d'un objet d'application 'UserInfo'qui contient une propriété SessionTokenIDV1que je ne souhaite pas exposer aux consommateurs de ma classe.

J'ai également besoin de la possibilité de définir cette valeur depuis ma classe.

Ma solution était d'encapsuler la propriété comme indiqué et de rendre le setter privé afin que je puisse définir la valeur du jeton de session sans permettre au code d'instanciation de le définir également (ou même de le voir dans mon cas)

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

Edit: Fixed Code Tag Edit: l'exemple contenait des erreurs qui ont été corrigées


-1

Crédits à https://www.dotnetperls.com/property .

les setters privés sont identiques aux champs en lecture seule. Ils ne peuvent être définis que dans le constructeur. Si vous essayez de définir de l'extérieur, vous obtenez une erreur de compilation.

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

Veuillez voir la capture d'écran ci-dessous lorsque j'ai essayé de le configurer depuis l'extérieur de la classe.

entrez la description de l'image ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.