Manish Basantani
Manish Basantani

Reputation: 17499

Readonly field that could be assigned a value outside constructor

Is there a way to have a private readonly field in a class that could be assigned a value anywhere in the class, but only once??

That is, I am looking for a private readonly kind of field which could be assigned a value only once, but not necessarily inside the constructor. So that, if a value is re-assigned to a field then it shows compile-time error ( I am sure that is asking for too much).

If there is any pattern (not language feature) that could do the same job, would really be interested in knowing that too.

Thanks for your interest.

Upvotes: 2

Views: 2654

Answers (7)

sergiol
sergiol

Reputation: 4337

I think I achieved a way to do it, if it is done in the constructor. It could also be on the variable declaration line. If done outside those contexts, you will receive an error: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

public class BillLine
{
    public BillLine(Bill bill)
    {
        m_Bill = bill;
    }

    private readonly Bill m_Bill;

    public Bill Bill
    {
        get { return m_Bill; }

        //supposed to be set only in the constructor
    }

    //(...)
}

Upvotes: 0

Sean U
Sean U

Reputation: 6850

You could create a generic struct to automate the process:

public struct WriteOnce<T>
{
    private T? _value;

    public T Value
    {
        get { return _value; }
        set
        {
            if (_value.HasValue) throw new InvalidOperationException();
            _value = value;
        }
    }
}

EDIT I just realized the above won't even compile. The compiler doesn't let _value's type to be Nullable, because the T in Nullable must be a non-nullable type.

Here's an implementation that will work a bit better:

public struct WriteOnce<T>
{
    private T _value;
    private bool _hasValue;

    public bool HasValue { get { return _hasValue; } }

    public T Value
    {
        get { return _value; }
        set
        {
            if (HasValue) throw new InvalidOperationException();
            _value = value;
            _hasValue = true;
        }
    }

    public static implicit operator T(WriteOnce<T> x)
    {
        return x.Value;
    }

    public WriteOnce(T val)
    {
        _value = val;
        _hasValue = true;
    }
}

Note I said it would work better - not that it would work well. There are still some gotchas on using it:

First, the compiler will complain if it detects that you're trying to use it without assigning something to it first. Initializing it like so will take care of that:

WriteOnce<int> foo = default(WriteOnce<int>);

Second, even if it throws an exception on modification, C# will still happily let you overwrite it. So while this does encapsulate some of the functionality; you should still wrap it in a property to prevent misuse if it's going to end up exposed in an object's interface.

private WriteOnce<int> _someInt = default(WriteOnce<int>);
public int SomeInt
{ 
    get { return _someInt; }
    set { _someInt.Value = value; }
}

However, that still doesn't really get around the last egregious error I committed in the original snippet, which was creating a mutable struct. If you're doing a lot of something like this it might be worth violating that principle for the sake of not repeating yourself, but this is definitely a potential trap that would need to be commented carefully and kept out of any public interfaces.

If it's just a one-off, though, what others have suggested about just directly implementing a property is less complicated and safer.

Upvotes: 1

Will Marcouiller
Will Marcouiller

Reputation: 24132

This looks like a good candidate for declaration assignment or singleton.

public class SomeClass {
    private readonly Type t = typeof(SomeClass);
}

Or otherwise, like the others said, make an internal or so property with only a setter, then assign a value to it.

public class SomeClass {
    private Type t;

    internal Type Type {
        set {
            if (t == null) t = value;
        }
    }
}

Upvotes: 0

Craig Suchanec
Craig Suchanec

Reputation: 10804

The short answer is no. The only place you can have a one-time assignment that is compiler checked is in the constructor. You could create a system where you'd get a run-time error if an assignment was attempted more than once but there is no C# construct to do this

Upvotes: 2

Brian Rasmussen
Brian Rasmussen

Reputation: 116401

There's no language support for that, but you could make a setter, which implemented the desired behavior. Obviously that would not give you compile time errors, but without the language support that is probably the best you can do.

Upvotes: 0

Oded
Oded

Reputation: 498914

You can use a private property that checks to see if it was assigned to and only assign a value if it hasn't been.

int? _writeOnceField;

private int? WriteOnce
{
   set
   {
      if (!_writeOnceFiled.HasValue)
      {
        writeOnceFiled = value;
      }
      else
      {
        // Throw exception
      }
   }
}

Upvotes: 0

Joel Martinez
Joel Martinez

Reputation: 47751

You won't get compile time error, but you can accomplish what you're looking for with a property. In the setter, only set the value if an internal bool/flag is false. Once you set it the first time, set the flag to true. And if the setter is invoked again by another piece of code, you can throw an InvalidOperationException to let them know it has already been initialized

private bool fieldAlreadySet = false;
private string field;

public string Field
{
   get {return field;}
   set
   {
       if (fieldAlreadySet) throw new InvalidOperationException("field already set");
       this.field = value;
       fieldAlreadySet = true;
   }
}

Upvotes: 5

Related Questions