简体   繁体   中英

variable that can't be modified

Does C# allow a variable that can't be modified? It's like a const , but instead of having to assign it a value at declaration, the variable does not have any default value, but can only be assigned a value once at runtime (EDIT: and possibly not from constructor). or is this not possible?

Yes, there are several ways to do that in C#.

First off, what is a "variable"? A variable is a storage location. Local variables, formal parameters of methods (and indexers, constructors and so on), static and instance fields, array elements and pointer dereferences are all variables.

Some variables can be declared as "readonly". A "readonly" variable can only be changed once, either by an initializer in the declaration, or in a constructor. Only fields declarations can be readonly; C# does not support user-declared readonly locals.

There are certain restrictions on readonly variables that help ensure that the normal operation of C# does not introduce a mutation. This can lead to some unexpected results! See

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

for details.

Some locals are effectively readonly as well. For example, when you say using(Stream s = whatever) then inside the embedded statement of the using you cannot change the value of s. The reason for this restriction is to prevent the bug whereby you create a resource that is to be disposed, and then dispose of a different resource when the contents of variable s are disposed. It had better be the same.

(Unfortunately there are bugs in C# involving the situation where the disposed resource is a struct type, the struct has a method which mutates the struct, and the local variable is or is not a closed-over local of an anonymous function or iterator block; since the scenarios are obscure and the fix would be potentially breaking we haven't done anything about it yet, pending further analysis.)

The local variable declared in a foreach statement is also effectively readonly -- that variable changes value every time through the loop, but you are not allowed to change its value.

There is no way to make a readonly formal parameter, array element or pointer dereference.

There are various ways to "break" the readonly restriction and write to a variable that is supposed to be read only. You can use Reflection or unsafe code to break pretty much any safety restriction of the CLR if you have sufficient privilege to do so. If it hurts when you do that, don't do that; with those powers comes the responsibility to know what you're doing and do it right.

您可以声明一个readonly字段变量,该变量只能在构造函数中设置或直接通过其声明设置。

Sure. You can use readonly :

Ie: public readonly int z;

This can only be modified from within the constructor.

From MSDN :

You can assign a value to a readonly field only in the following contexts:

When the variable is initialized in the declaration, for example:

  • public readonly int y = 5;

  • For an instance field, in the instance constructors of the class that contains the field declaration, or for a static field, in the static constructor of the class that contains the field declaration. These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

If however you are wanting to make a property that can only be altered within the class that created it, you can use the following:

public string SetInClass
{
   get;
   private set;
}

This allows changes to be made within the class, but the variable cannot be altered from outside the class.

You can roll your own using a custom setter (but don't use Object unless you have to, choose the correct class):

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:

This doesn't stop the private field being changed by the class internals.

You could create your own generic class that provided this functionality, but that might be overkill.

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

Since a similar question has been recently asked by @Ivan , let me suggest yet another method to instanciate a property at any place in the code , of course, more exactly, with the support of a generic constructor.

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

Usage would be

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

The point is that now you get a compile error if you try to set a new value.

Aside from that, I believe that you better off using a functional language like F# instead of C# if you want to follow that paradigm.

You can define a readonly variable that can only have it's value set in the objects constructor.

Read about it here: http://msdn.microsoft.com/en-us/library/acdd6hb7%28v=VS.100%29.aspx

It is possible to mark a field readonly which will require you set it a value either at point of declaration or in the constructor and then will prevent it from being reassigned post construction.

However, whilst the reference will be read-only, the object will not necessarily be so too. To prevent the object itself from being modified you will have to make the type immutable or else provide an wrapper class that only exposes the non-destructive methods and properties of the underlying type.

If you want to assign the variable at runtime after the object containing it has been constructed you could use a custom property with a setter method that can only be modified once. ie.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM