2012-02-08 2 views
3

Est-il possible d'avoir une variable qui est une variable d'instance dans une classe mais qui ne peut être accédée que par une propriété spécifique?Variable à usage privé utilisée uniquement dans la propriété

Je crée assez souvent « auto-créer des » propriétés comme si ...

private IWGSLocation _location; 
public IWGSLocation Location 
{ 
    get 
    { 
     _location = _location ?? new WGSLocation(); 
     _location.Latitude = Latitude.GetValueOrDefault(0); 
     _location.Longitude = Longitude.GetValueOrDefault(0); 
     return _location; 
    } 
} 

ce qui veut dire que je ne vais pas recréer une nouvelle WGSLocation (ou tout autre type d'objet dont j'ai besoin, ce qui peut être coûteux à créer, ou peut seulement avoir besoin d'être créé une fois) chaque fois que j'accède à la propriété. L'inconvénient est que ma classe peut accéder à la variable _location. Mais je ne le veux pas vraiment, donc s'il y a une façon d'avoir une variable d'instance qui ne peut être utilisée que dans la propriété elle-même?

Je pense quelque chose le long de ces lignes ...

public IWGSLocation Location 
{ 
    get 
    { 
     WGSLocation _location = _location ?? new WGSLocation(); 
     _location.Latitude = Latitude.GetValueOrDefault(0); 
     _location.Longitude = Longitude.GetValueOrDefault(0); 
     return _location; 
    } 
} 
+1

Je préfère utiliser les propriétés paresseuses initialisées lorsque j'ai mesuré que la création désirée de celui-ci est un goulot d'étranglement dans mon application. – CodesInChaos

Répondre

5

Je suis d'accord que ce serait une belle fonctionnalité de langage pour avoir habitants persistants - c'est, les variables dont la durée de vie sont basées sur la durée de vie de l'instance mais dont portées (la région du texte du programme dans lequel il est légal d'accéder à la variable par son nom) sont locaux. Ce serait bien d'avoir aussi des locaux «statiques», comme le font certaines langues. Malheureusement, ce n'est pas une caractéristique de C# et nous n'avons pas l'intention de l'ajouter. C'est agréable à avoir, mais agréable d'avoir n'est pas assez bon pour justifier la dépense, ou de reporter ou d'annuler une fonctionnalité "plus agréable d'avoir".

C'est seulement "agréable d'avoir" parce que bien sûr si vous avez un champ privé, c'est déjà un détail d'implémentation privée de la classe. Si vous ne voulez pas qu'il soit utilisé à l'extérieur de la propriété, alors n'écrit pas de code qui l'utilise en dehors de la propriété.Si l'un de vos collègues essaye de le faire, mettez-le dans la revue de code.

Je pensais pouvoir ajouter: soyez très prudent lorsque vous écrivez des getters de propriétés qui se transforment en état. Par apporteurs de propriété par défaut sont évalués en regardant un objet dans le débogueur, et il peut être très déroutant être débogage quelque chose et le débogueur changer les valeurs des champs juste parce que vous êtes examiner un objet.

+0

bon point concernant le débogueur. J'utilise rarement le débogueur ces jours-ci, alors j'avais oublié ce truc. –

+0

"Si l'un de vos collègues essaye de le faire, mettez-le dans la revue de code." J'AIME ÇA! –

+0

Désolé, je parierais que la plupart du code ne voit jamais une révision de code et personnellement je n'ai jamais aimé claquer mes collègues. Toujours semble revenir à moi. Curieux de voir ce que vous pensez de ma réponse. – AnthonyVO

1

Le fait que la classe peut accéder est pas nécessairement un inconvénient. Il est toujours logiquement encapsulé dans la même entité.

Ce que vous recherchez après n'est pas possible comme vous le souhaitez. Les variables membres sont visibles par toutes les zones de la classe et les variables locales sont limitées à leur étendue définie.

Ce que vous pouvez faire à la place est de placer l'emplacement dans une classe de conteneur. Cette classe est votre variable membre. Lors du retour du IWGSLocation vous percez simplement dans la classe conteneur:

public class LocationContainer 
{ 
    public IWGSLocation InnerLocation { get; private set; } 

    public void SetLocation(WGSLocation loc) 
    { 
     InnerLocation = loc; 
    } 
} 

private readonly LocationContainer _container = new LocationContainer(); 

public IWGSLocation Location 
{ 
    get 
    { 
     if (_container.InnerLocation == null) 
     { 
      _container.SetLocation(...); 
     } 

     return _container.InnerLocation; 
    } 
} 

Cela n'arrêtera pas la classe de toucher _container, mais il fera d'autres développeurs réfléchir à deux fois avant de le faire et ils ne seront pas en mesure accidentellement muter l'emplacement sans appeler explicitement SetLocation.

Vous pourriez même mettre une fois ensemble garde dans le SetLocation du récipient.

Mise à jour: je réellement utiliser la classe paresseuse ici, quelque chose comme:

private readonly Lazy<IWGSLocation> _location = new Lazy<IWGSLocation>(() 
=> 
{ 
    var l = new WGSLocation(); 
    l.Latitude = Latitude.GetValueOrDefault(0); 
    l.Longitude = Longitude.GetValueOrDefault(0); 
    return l; 
}); 

public IWGSLocation Location 
{ 
    get { return _location.Value; } 
} 

attention, cela a été compilé la tête! :-)

+0

+1 pour une bonne solution. mais encore un peu bavard! Je préférerais qu'il y ait un moyen de le faire dans la langue :) à moins bien sûr que ce que je fais soit fondamentalement faux d'une certaine façon. –

+0

@AntonyScott Oui c'est un petit mot, mais si vous essayez de protéger des éléments d'une classe d'autres éléments de la même classe, je dirais que vous devriez revoir cela pour voir si vous avez besoin de deux classes. Je suppose qu'il y a un argument pour que votre exigence soit fausse, mais je ne me soucie pas de ces discussions. –

+0

@AntonyScott En fait, utilisez simplement la classe paresseuse. –

1

Votre implémentation actuelle m'a l'air brisée.

var x=obj.Location; 
x.Latitude = 1; 
Console.WriteLine(x.Latitude);//1 
var y=obj.Location; 
Console.WriteLine(x.Latitude);//WTF it changed 

Je recommande de faire IWGSLocation immuable, ou seulement la modifier sur la création, en fonction de la sémantique que vous voulez.

+0

ah, oui. tu as raison. Je l'ai changé pour utiliser un opérateur de coalescence nulle. J'avais l'habitude de créer seulement l'objet s'il n'avait pas déjà été défini. bon endroit :) –

Questions connexes