views:

234

answers:

8
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?
+9  A: 

You can declare a readonly variable which can be set only in the constructor or directly through its declaration.

Oliver Hanappi
so it has to be from a constructor?
Louis Rhys
You can restrict access and only allow setting the field within your class, but `readonly` and `const` are the only language constructs which actually permit setting the value after construction or declaration.
Oliver Hanappi
Yes, either its constructor or inline within the declaration (but just like a const then). Also it has to be within the constructor, not just at construction time - that means it is not possible to set the value of a readonly variable within a function called by the constructor.
Paul Hadfield
+4  A: 

Sure. You can use readonly:

I.e.: 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.

Kyle Rozendo
A: 

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

Rune Grimstad
+1  A: 

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.

Paul Ruane
+5  A: 

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 InvalidOperationExceotion("This value is read only");
        this.myObj = value;
        this.myObjSet = true;
    }
}

EDIT (as per comment):

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

ck
@Kyle, Why? if you look at the edit above it says it possibly wont be set from the constructor which rules out readonly! looks like a valid implementation to me given the OP's requirements!
OneSHOT
wow great idea!
Louis Rhys
@Louis, as Kyle and OneSHOT say there is nothing stopping the class changing the value as many times as it wasnts as it can access the private variable directly.
Paul Hadfield
Removed comment, didn't see the edit - Nice one, as long as the private variable cannot be set back to null.
Kyle Rozendo
@paul: yeah that's true...
Louis Rhys
A: 

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;
}
Andy Rose
+1  A: 

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;
      }
   }
}
Paul Hadfield
you forgot some quotation marks
Louis Rhys
@Louis, have updated, it should be syntax correct but I did type directly as a code block as part of the answer - I wouldn't recommend SO as a code editor - No source control integration for one thing :)
Paul Hadfield
In order to be fair I acccept this because this is valid given the requirement in my question.. even though actually I like ck's answer more and it is actually what I need
Louis Rhys
+2  A: 

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://blogs.msdn.com/b/ericlippert/archive/2008/05/14/mutating-readonly-structs.aspx

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.

Eric Lippert
+1 for explaining a bit more on the internals of C#.. Very educative!
Arcturus
"Only fields declarations can be readonly; C# does not support user-declared readonly locals." Although primitive, string, enum and ValueType derived locals can be declared *const* which is effectively the same thing for these types (although arguably it is closer to a literal aliasing than a read-only, local variable).
Paul Ruane
@Paul: But of course those are not *variables*. Constants are not variables because *constant* and *variable* are *opposites*. (And you are incorrect to say that ValueType-derived locals can be constants. I assure you they cannot. I suspect you are thinking of some other language; perhaps C++.)
Eric Lippert
@Eric: I was only pointing out that a variable can be swapped for a const in some situations to get behaviour similar to what OP was asking for. Bizarre on the ValueType front, I could have sworn I had done that before (perhaps I did and it didn't compile!).
Paul Ruane