Y a-t-il des raisons d'utiliser des propriétés privées en C #?

246

Je viens de réaliser que la construction de la propriété C # peut également être utilisée avec un modificateur d'accès privé :

private string Password { get; set; }

Bien que cela soit techniquement intéressant, je ne peux pas imaginer quand je l'utiliserais car un domaine privé implique encore moins de cérémonie :

private string _password;

et je ne peux pas imaginer quand j'aurais besoin de pouvoir obtenir en interne mais pas définir ou définir mais pas obtenir un champ privé:

private string Password { get; }

ou

private string Password { set; }

mais il y a peut-être un cas d'utilisation avec des classes imbriquées / héritées ou peut-être où un get / set peut contenir de la logique au lieu de simplement restituer la valeur de la propriété, bien que j'aurais tendance à garder les propriétés strictement simples et à laisser les méthodes explicites faire de la logique, par exemple GetEncodedPassword().

Quelqu'un utilise-t-il des propriétés privées en C # pour quelque raison que ce soit ou s'agit-il simplement d'une de ces constructions de code techniquement possibles, mais rarement utilisées dans le code réel?

Addenda

Jolies réponses, en les lisant, j'ai choisi ces utilisations pour les propriétés privées:

  • lorsque les champs privés doivent être chargés paresseusement
  • lorsque les champs privés nécessitent une logique supplémentaire ou sont des valeurs calculées
  • car les champs privés peuvent être difficiles à déboguer
  • afin de "se présenter un contrat"
  • pour convertir / simplifier en interne une propriété exposée dans le cadre de la sérialisation
  • encapsuler des variables globales à utiliser dans votre classe
Edward Tanguay
la source
La technique encouragée par les propriétés privées est l' auto-encapsulation - voir: sourcemaking.com/refactoring/self-encapsulate-field
LBushkin

Réponses:

212

Je les utilise si j'ai besoin de mettre en cache une valeur et que je veux la charger paresseusement.

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}
Shaun Bowe
la source
42
un joli motif commun pour cela estreturn _password ?? (_password = CallExpensiveOperation());
Marc
1
@EvAlex Lazy <T> pourrait être préférable si vous pouvez l'utiliser. Mais il ne peut utiliser que des éléments statiques, vous ne pouvez donc pas accéder aux méthodes (non statiques) ou à d'autres membres. Au fait, je préfère la syntaxe à return _password ?? (_password = CallExpensiveOperation());une seule ligne depuis un moment. Ou en fait Resharper le préfère :).
Bart
4
@Marc depuis C # 6, vous n'avez même plus besoin d'écrire un get et un return. private string Password => _password ?? (_password = CallExpensiveOperation());
Otto Abnormalverbraucher
1
Avec C # 8, c'est encore plus court:private string Password => _password ??= CallExpensiveOperation();
Dave M
2
@Bas Ce n'est pas un chargement paresseux car il CallExpensiveOperation();est appelé pendant la construction / l'initialisation de l'objet contenant et non pas lors du premier accès à la propriété.
Stefan Podskubka
142

L'utilisation principale de ceci dans mon code est l'initialisation paresseuse, comme d'autres l'ont mentionné.

Une autre raison des propriétés privées sur les champs est que les propriétés privées sont beaucoup plus faciles à déboguer que les champs privés. Je veux souvent savoir des choses comme "ce champ est défini de manière inattendue; qui est le premier appelant qui définit ce champ?" et c'est beaucoup plus facile si vous pouvez simplement mettre un point d'arrêt sur le setter et appuyer sur go. Vous pouvez y mettre la connexion. Vous pouvez y mettre des mesures de performances. Vous pouvez effectuer des vérifications de cohérence qui s'exécutent dans la version de débogage.

Fondamentalement, cela se résume à: le code est beaucoup plus puissant que les données . Toute technique qui me permet d'écrire le code dont j'ai besoin est bonne. Les champs ne vous permettent pas d'écrire du code, contrairement aux propriétés.

Eric Lippert
la source
5
Est-ce que "le code est bien plus puissant que les données", vous dites? La recherche sur Google renvoie des références qui pointent vers vous. Je veux juste savoir pour que je puisse le citer correctement quand j'en ai besoin.
Joan Venge
24
@Joan: Je ne sais pas. Soit je l'ai inventé, soit j'ai entendu quelqu'un d'autre le dire et je me suis dit "wow, je devrais totalement voler ça, puis oublier tout de qui je l'ai volé."
Eric Lippert
1
+ CodeLens vous indique où la propriété est référencée
UuDdLrLrSs
43

il y a peut-être un cas d'utilisation avec des classes imbriquées / héritées ou peut-être où un get / set peut contenir de la logique au lieu de simplement redonner la valeur de la propriété

Personnellement, j'utilise cela même lorsque je n'ai pas besoin de logique sur le getter ou le setter d'une propriété. L'utilisation d'une propriété, même privée, aide à pérenniser votre code afin que vous puissiez ajouter la logique à un getter plus tard, si nécessaire.

Si je pense qu'une propriété peut éventuellement nécessiter une logique supplémentaire, je l'enveloppe parfois dans une propriété privée au lieu d'utiliser un champ, juste pour ne pas avoir à modifier mon code plus tard.


Dans un cas semi-connexe (bien que différent de votre question), j'utilise très fréquemment les setters privés sur les propriétés publiques:

public string Password 
{
    get; 
    private set;
}

Cela vous donne un getter public, mais garde le setter privé.

Reed Copsey
la source
+1 est logique: "Si je pense qu'une propriété peut éventuellement nécessiter une logique supplémentaire, je l'enveloppe parfois dans une propriété privée au lieu d'utiliser un champ, pour ne pas avoir à modifier mon code plus tard."
Edward Tanguay du
7
setters privés <3
Earlz
20

Les valeurs calculées sont une bonne utilisation pour les propriétés privées uniquement. Plusieurs fois, j'ai eu des propriétés privées en lecture seule et je fais juste un calcul sur d'autres champs de mon type. Ce n'est pas digne d'une méthode et n'est pas intéressant pour les autres classes, donc c'est une propriété privée.

JaredPar
la source
20

L'initialisation paresseuse est un endroit où ils peuvent être nets, par exemple

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

Ensuite, vous pouvez écrire: this.MyTypepartout plutôt que this.mytype.Valueet résumer le fait qu'il est instancié paresseusement en un seul endroit.

Une chose est dommage, c'est que C # ne prend pas en charge l'étendue du champ de sauvegarde sur la propriété (c'est-à-dire le déclarer dans la définition de propriété) pour le masquer complètement et s'assurer qu'il ne peut être accessible que via la propriété.

Greg Beech
la source
2
D'accord, ce serait d'avoir l'aspect de la portée là-bas.
Chris Marisic
5
J'utilise fréquemment cette même technique et j'ai également souhaité qu'un champ puisse être délimité par un corps de code. C'est une fonctionnalité intéressante mais de faible priorité.
Eric Lippert
5
@Eric Lippert - field-declarationle champ d'application à l'intérieur accessor-declarationsa classé le numéro 1 sur ma liste de souhaits C # depuis longtemps. Si vous pouviez le faire concevoir et implémenter dans une future version (réelle), alors je vous ferai un gâteau.
Jeffrey L Whitledge
13

La seule utilisation à laquelle je peux penser

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}
Lukasz Madon
la source
+1 pour la classe utile de propriétés calculées à partir d'autres variables privées
Daren Thomas
2
Pourquoi ne pas utiliser une méthode privéeprivate bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman
10

Les propriétés et les champs ne sont pas un à un. Une propriété concerne l'interface d'une classe (qu'il s'agisse de son interface publique ou interne), tandis qu'un champ concerne l'implémentation de la classe. Les propriétés ne doivent pas être considérées comme un moyen d'exposer simplement des champs, elles doivent être considérées comme un moyen d'exposer l'intention et le but de la classe.

Tout comme vous utilisez des propriétés pour présenter un contrat à vos consommateurs sur ce qui constitue votre classe, vous pouvez également vous présenter un contrat pour des raisons très similaires. Alors oui, j'utilise des propriétés privées quand cela a du sens. Parfois, une propriété privée peut masquer des détails d'implémentation tels que le chargement paresseux, le fait qu'une propriété est vraiment un conglomérat de plusieurs champs et aspects, ou qu'une propriété doit être virtuellement instanciée à chaque appel (pensez DateTime.Now). Il y a certainement des moments où il est logique d'imposer cela même sur vous-même dans le backend de la classe.

Matt Greer
la source
+1: "vous pouvez également vous présenter un contrat pour des raisons très similaires" est logique
Edward Tanguay
8

Je les utilise en sérialisation, avec des choses comme DataContractSerializerou protobuf-net qui supportent cette utilisation ( XmlSerializerne le fait pas). Il est utile si vous devez simplifier un objet dans le cadre de la sérialisation:

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}
Marc Gravell
la source
6

Une chose que je fais tout le temps est de stocker des variables / cache "globales" dans HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}
Earlz
la source
5

Je les utilise de temps en temps. Ils peuvent faciliter le débogage lorsque vous pouvez facilement insérer un point d'arrêt dans la propriété ou ajouter une instruction de journalisation, etc.

Peut également être utile si vous devez ultérieurement modifier le type de vos données d'une manière ou si vous devez utiliser la réflexion.

Hans Olsson
la source
Idem; s'il y a une logique impliquée dans un get / set, je peux parfois utiliser une propriété privée ou protégée. Cela dépend généralement de la quantité de logique: logique simple que je ferai dans la propriété, beaucoup de logique que j'utiliserai généralement une fonction auxiliaire. Tout ce qui rend le code le plus facile à maintenir.
TechNeilogy
5

J'utilise des propriétés privées pour réduire le code d'accès aux sous-propriétés souvent utilisées.

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

Il est utile s'il existe de nombreuses sous-propriétés.

Yohanes Nurcahyo
la source
2

Il est courant de modifier uniquement les membres avec des méthodes get / set, même privées. Maintenant, la logique derrière cela est que vous savez que votre get / set se comporte toujours d'une manière particulière (par exemple, déclencher des événements) qui ne semble pas avoir de sens car ceux-ci ne seront pas inclus dans le schéma de propriété ... mais les vieilles habitudes ont la vie dure.

corsiKa
la source
2

Il est parfaitement logique lorsqu'il existe une logique associée à la propriété set ou get (pensez à l'initialisation paresseuse) et que la propriété est utilisée à quelques endroits de la classe.

Si c'est juste un terrain de support droit? Rien ne me vient à l'esprit comme une bonne raison.

Marc
la source
2

Je sais que cette question est très ancienne mais les informations ci-dessous ne figuraient dans aucune des réponses actuelles.

Je ne peux pas imaginer quand j'aurais besoin de pouvoir obtenir en interne mais pas

Si vous injectez vos dépendances, vous voudrez peut-être avoir un Getter sur une propriété et non un setter car cela dénoterait une propriété en lecture seule. En d'autres termes, la propriété ne peut être définie que dans le constructeur et ne peut être modifiée par aucun autre code de la classe.

Visual Studio Professional fournira également des informations sur une propriété et non sur un champ, ce qui facilitera la visualisation de l'utilisation de votre champ.

PorpField

Gars
la source
1

Eh bien, comme personne ne l'a mentionné, vous pouvez l'utiliser pour valider des données ou pour verrouiller des variables.

  • Validation

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • Verrouillage

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    Remarque: Lorsque vous verrouillez une référence, vous ne verrouillez pas l'accès aux membres de l'objet référencé.

Référence paresseuse: Lors du chargement paresseux, vous devrez peut-être le faire en mode asynchrone pour lequel il existe de nos jours AsyncLazy . Si vous utilisez des versions antérieures à Visual Studio SDK 2015 ou ne l'utilisez pas, vous pouvez également utiliser AsyncLazy d'AsyncEx .

Lucas Martins Juviniano
la source
0

Certaines utilisations plus exotiques des champs explicites incluent:

  • vous devez utiliser refou outavec la valeur - peut-être parce que c'est unInterlocked compteur
  • il est destiné à représenter la mise en page fondamentale, par exemple sur une structmise en page explicite (peut-être pour mapper vers un vidage C ++, ouunsafe code)
  • historiquement, le type a été utilisé avec BinaryFormatterla gestion automatique des champs (le passage aux accessoires automatiques modifie les noms et rompt ainsi le sérialiseur)
Marc Gravell
la source