Well, I'm not sure about volatility, but if you don't mind a little abuse and call the second method ... (also it does not depend on nullability, it is freely used for value types) Avoids null checks in getter too. Only locking is performed when writing, so AFAIK, the only negative effect occurs when a delegate is called when a value is received.
public class SetOnce<T> { private static readonly Func<T> NoValueSetError = () => { throw new Exception("Value not yet present.");}; private Func<T> ValueGetter = NoValueSetError; private readonly object SetterLock = new object(); public T SetValue(T newValue) { lock (SetterLock) { if (ValueGetter != NoValueSetError) throw new Exception("Value already present."); else ValueGetter = () => newValue; } return newValue; } public T GetValue() { return ValueGetter(); } }
Actually, I feel really stupid about it and feel a little offensive. I would be interested to see comments about possible issues with this. :)
EDIT: just realized that this means that the first call to SetValue(null) means that "null" will be considered a valid value and will return null without exception. Not sure if this is what you would like (I donβt understand why null cannot be a valid value, but if you want to avoid this, just do the check in the installer, there is no need for getter)
EDITx2: if you still want to limit it to class and avoid null values, a simple change could be:
public class SetOnce<T> where T : class { private static readonly Func<T> NoValueSetError = () => { throw new Exception("Value not yet present.");}; private Func<T> ValueGetter = NoValueSetError; private readonly object SetterLock = new object(); public T SetValue(T newValue) { if (newValue == null) throw new ArgumentNullException("newValue"); lock (SetterLock) { if (ValueGetter != NoValueSetError) throw new Exception("Value already present."); else ValueGetter = () => newValue; } return newValue; } public T GetValue() { return ValueGetter(); } }
source share