2010-09-07 9 views
12

Est-ce que C# autorise une variable qui ne peut pas être modifiée? C'est comme const, mais au lieu d'avoir à lui affecter une valeur à la déclaration, la variable n'a pas de valeur par défaut, mais ne peut être affectée qu'une seule fois à l'exécution (EDIT: et peut-être pas du constructeur). ou n'est-ce pas possible?variable qui ne peut pas être modifiée

+12

Nitpick linguistique: "une variable qui ne peut pas être modifiée" est un oxymore. – LukeH

Répondre

5

Vous pouvez créer votre propre classe générique qui a fourni cette fonctionnalité, mais cela pourrait être excessif.

public class SetValueOnce<T> 
{ 
    public bool _set; 
    private T _value; 

    public SetValueOnce() 
    { 
     _value = default(T); 
     _set = false; 
    } 

    public SetValueOnce(T value) 
    { 
     _value = value; 
     _set = true; 
    } 

    public T Value 
    { 
     get 
     { 
      if(!_set) 
      throw new Exception("Value has not been set yet!"); 
      return _value; 
     { 
     set 
     { 
     if(_set) 
      throw new Exception("Value already set!"); 
     _value = value; 
     _set = true; 
     } 
    } 
} 
+0

vous avez oublié quelques guillemets –

+0

@Louis, ont mis à jour, il devrait être syntaxe correcte, mais j'ai fait taper directement comme un bloc de code dans le cadre de la réponse - Je ne recommanderais pas SO comme un éditeur de code - aucune intégration de contrôle de source pour une chose :) –

+0

Pour être juste je l'accepte parce que cela est valide étant donné l'exigence dans ma question .. même si en fait j'aime la réponse de ck plus et c'est en fait ce dont j'ai besoin –

12

Vous pouvez déclarer une variable readonly qui ne peut être définie que dans le constructeur ou directement via sa déclaration.

+0

donc il doit provenir d'un constructeur? –

+0

Vous pouvez restreindre l'accès et autoriser uniquement la définition du champ dans votre classe, mais 'readonly' et' const' sont les seules constructions de langage qui permettent réellement de définir la valeur après la construction ou la déclaration. –

+1

Oui, soit son constructeur ou en ligne dans la déclaration (mais juste comme un const alors). Il doit aussi être dans le constructeur, pas seulement au moment de la construction - cela signifie qu'il n'est pas possible de définir la valeur d'une variable readonly dans une fonction appelée par le constructeur. –

4

Bien sûr. Vous pouvez utiliser readonly:

i.e. .: public readonly int z;

Cela ne peut être modifié à partir du constructeur.

De MSDN:

Vous pouvez attribuer une valeur à un champ en lecture seule uniquement dans les contextes suivants:

Lorsque la variable est initialisée dans la déclaration, par exemple:

  • public readonly int y = 5;

  • Pour un champ d'instance, dans les constructeurs d'instance de la classe t hat contient la déclaration de champ, ou pour un champ statique, dans le constructeur statique de la classe qui contient la déclaration de champ. Ce sont également les seuls contextes dans lesquels il est valide de passer un champ en lecture seule comme paramètre out ou ref.

Si toutefois vous êtes désireux de faire une propriété qui ne peut être modifié dans la classe qui l'a créé, vous pouvez utiliser les éléments suivants:

public string SetInClass 
{ 
    get; 
    private set; 
} 

Cela permet des modifications à apporter au sein de la classe , mais la variable ne peut pas être être modifiée de l'extérieur de la classe.

1

Il est possible de marquer un champ readonly qui vous demandera de définir une valeur, soit au moment de la déclaration ou dans le constructeur et l'empêchera d'être réaffecté après la construction.

Cependant, alors que la référence sera en lecture seule, l'objet ne le sera pas nécessairement aussi. Pour éviter que l'objet lui-même ne soit modifié, vous devrez rendre le type immuable ou fournir une classe wrapper qui n'expose que les méthodes non destructives et les propriétés du type sous-jacent.

5

Vous pouvez rouler votre propre à l'aide d'un setter personnalisé (mais ne pas utiliser Object à moins que vous devez, choisir la bonne classe):

private Object myObj = null; 
private Boolean myObjSet = false; 

public Object MyObj 
{ 
    get { return this.myObj; } 
    set 
    { 
     if (this.myObjSet) throw new InvalidOperationException("This value is read only"); 
     this.myObj = value; 
     this.myObjSet = true; 
    } 
} 

EDIT:

Ce n » t arrêter le champ privé étant changé par les internes de classe.

+1

@Kyle, pourquoi? Si vous regardez l'édition ci-dessus, il est possible que ce soit le constructeur qui exclut readonly! ressemble à une implémentation valide pour moi étant donné les exigences du PO! – OneSHOT

+0

wow bonne idée! –

+2

@Louis, comme Kyle et OneSHOT disent qu'il n'y a rien qui empêche la classe de changer la valeur autant de fois qu'elle ne l'était parce qu'elle peut accéder directement à la variable privée. –

0

Si vous souhaitez affecter la variable à l'exécution après la construction de l'objet qui la contient, vous pouvez utiliser une propriété personnalisée avec une méthode setter qui ne peut être modifiée qu'une seule fois. c'est à dire.

private myVariable = null; 
public object MyVariable 
{ 
    get { return myVariable; } 
    set { if(myVariable == null) myVariable = value; 
} 
23

Oui, il existe plusieurs façons de le faire en C#.

Tout d'abord, qu'est-ce qu'une "variable"? Une variable est un emplacement de stockage. Les variables locales, les paramètres formels des méthodes (et indexeurs, constructeurs et ainsi de suite), les champs statiques et d'instance, les éléments de tableau et les déréférences de pointeurs sont toutes des variables.

Certaines variables peuvent être déclarées comme "en lecture seule". Une variable "readonly" ne peut être modifiée qu'une seule fois, soit par un initialiseur dans la déclaration, soit dans un constructeur. Seules les déclarations de champs peuvent être en lecture seule; C# ne prend pas en charge les locals readonly déclarés par l'utilisateur.

Il existe certaines restrictions sur les variables en lecture seule qui permettent de garantir que le fonctionnement normal de C# n'introduit pas de mutation. Cela peut conduire à des résultats inattendus! Voir

http://ericlippert.com/2008/05/14/mutating-readonly-structs/

pour plus de détails.

Certains locaux sont effectivement en lecture seule. Par exemple, lorsque vous dites using(Stream s = whatever), vous ne pouvez pas modifier la valeur de s dans l'instruction incorporée du using. La raison de cette restriction est d'empêcher le bogue par lequel vous créez une ressource qui doit être éliminée, puis de disposer d'une ressource différente lorsque le contenu de la variable s est éliminé. Il vaudrait mieux être pareil.

(Malheureusement, il existe des bogues en C# impliquant la situation où la ressource disposée est un type struct, la structure a une méthode qui mute la structure, et la variable locale est ou n'est pas un local fermé d'une fonction anonyme . ou bloc iterator, puisque les scénarios sont obscurs et le correctif seraient potentiellement briser, nous avons rien fait encore, en attendant une analyse plus approfondie)

la variable locale déclarée dans un communiqué foreach est aussi efficace readonly - que variable change la valeur chaque fois à travers la boucle, mais vous ne sont pas autorisés à changer sa valeur.

Il est impossible de créer un paramètre formel en lecture seule, un élément de tableau ou un déréférencement de pointeur.

Il existe plusieurs façons de "casser" la restriction en lecture seule et d'écrire dans une variable supposée être en lecture seule.Vous pouvez utiliser Reflection ou un code non sécurisé pour casser à peu près toutes les restrictions de sécurité du CLR si vous avez suffisamment de privilèges pour le faire. Si ça fait mal quand vous faites cela, ne faites pas ça; avec ces pouvoirs vient la responsabilité de savoir ce que vous faites et de le faire correctement.

+1

+1 pour expliquer un peu plus sur les internes de C# .. Très éducatif! – Arcturus

+0

"Seules les déclarations de champs peuvent être en lecture seule, C# ne prend pas en charge les locals readonly déclarés par l'utilisateur." Bien que les locals dérivés de type primitive, string, enum et ValueType puissent être déclarés * const *, ce qui est effectivement la même chose pour ces types (bien que ce soit plus proche d'un aliasing littéral que d'une variable locale en lecture seule). –

+0

@Paul: Mais bien sûr, ce ne sont pas * variables *. Les constantes ne sont pas des variables car * constante * et * variable * sont * opposées *. (Et vous avez tort de dire que les locals dérivés de ValueType peuvent être des constantes, je vous assure qu'ils ne le peuvent pas, je pense que vous pensez à d'autres langages, peut-être C++.) –

1

Depuis un similar question a été récemment posée par @Ivan, permettez-moi de suggérer une autre méthode pour instancier une propriété à tout endroit dans le code, bien sûr, plus exactement, avec le soutien d'un constructeur générique.

public class Immutable<T> { 
    public T Val { get; private set; } 
    public Immutable(T t) { 
     Val = t; 
    } 
} 

utilisation serait

var immutableInt1 = new Immutable<int>(3); // you can set only once 
immutableInt1.Val = 5; // compile error here: set inaccessible 
Console.WriteLine("value: " + immutableInt1.Val); 

Le point est que maintenant vous obtenez une erreur compilez si vous essayez de définir une nouvelle valeur. En dehors de cela, je crois que vous préférez utiliser un langage fonctionnel comme F # au lieu de C# si vous voulez suivre ce paradigme.

Questions connexes