Readonly field that could be assigned a value outside constructor

后端 未结 7 1892
清酒与你
清酒与你 2021-01-22 16:19

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 read

相关标签:
7条回答
  • 2021-01-22 16:42

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-22 16:44

    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.

    0 讨论(0)
  • 2021-01-22 16:57

    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

    0 讨论(0)
  • 2021-01-22 16:57

    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.

    0 讨论(0)
  • 2021-01-22 17:02

    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
          }
       }
    }
    
    0 讨论(0)
  • 2021-01-22 17:07

    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
        }
    
        //(...)
    }
    
    0 讨论(0)
提交回复
热议问题