C# 有没有一种方法可以在C中只设置一次属性#

C# 有没有一种方法可以在C中只设置一次属性#,c#,.net,C#,.net,我正在寻找一种方法,允许C#对象中的属性只设置一次。编写代码来实现这一点很容易,但如果存在标准机制,我宁愿使用标准机制 public OneShot<int> SetOnceProperty { get; set; } 公共一次性SetOnceProperty{get;set;} 我想做的是,如果尚未设置属性,则可以设置该属性,但如果之前已设置该属性,则会引发异常。它的功能应该像一个可为空的值,我可以检查它是否已设置。在.NET 4.0中的TPL中对此有直接的支持 (编辑:上面这句话

我正在寻找一种方法,允许C#对象中的属性只设置一次。编写代码来实现这一点很容易,但如果存在标准机制,我宁愿使用标准机制

public OneShot<int> SetOnceProperty { get; set; } 公共一次性SetOnceProperty{get;set;}
我想做的是,如果尚未设置属性,则可以设置该属性,但如果之前已设置该属性,则会引发异常。它的功能应该像一个可为空的值,我可以检查它是否已设置。

在.NET 4.0中的TPL中对此有直接的支持

(编辑:上面这句话是为了预测
System.Threading.WriteOnce
,它存在于当时可用的“预览”位中,但这似乎在TPL达到RTM/GA之前就消失了)

在那之前,你自己检查一下。。。据我回忆,这句话不多

比如:

public sealed class WriteOnce<T>
{
    private T value;
    private bool hasValue;
    public override string ToString()
    {
        return hasValue ? Convert.ToString(value) : "";
    }
    public T Value
    {
        get
        {
            if (!hasValue) throw new InvalidOperationException("Value not set");
            return value;
        }
        set
        {
            if (hasValue) throw new InvalidOperationException("Value already set");
            this.value = value;
            this.hasValue = true;
        }
    }
    public T ValueOrDefault { get { return value; } }

    public static implicit operator T(WriteOnce<T> value) { return value.Value; }
}
公共密封类写入
{
私人T值;
私有布尔值;
公共重写字符串ToString()
{
返回hasValue?Convert.ToString(值):“”;
}
公共价值
{
得到
{
如果(!hasValue)抛出新的InvalidOperationException(“未设置值”);
返回值;
}
设置
{
如果(hasValue)抛出新的InvalidOperationException(“值已设置”);
这个值=值;
this.hasValue=true;
}
}
公共T值默认值{get{return value;}}
公共静态隐式运算符T(WriteOnce值){return value.value;}
}
然后使用,例如:

readonly WriteOnce<string> name = new WriteOnce<string>();
public WriteOnce<string> Name { get { return name; } }
readonly WriteOnce name=new WriteOnce();
公共写入名称{get{return Name;}}
您可以自己动手(请参阅答案的末尾,了解线程安全且支持默认值的更健壮的实现)

public类SetOnce
{
私有布尔集;
私人T值;
公共价值
{
获取{返回值;}
设置
{
如果(设置)抛出新的AlreadySetException(值);
设置=真;
这个值=值;
}
}
公共静态隐式运算符T(SetOnce toConvert)
{
返回convert.value;
}
}
您可以这样使用它:

public class Foo
{
    private readonly SetOnce<int> toBeSetOnce = new SetOnce<int>();

    public int ToBeSetOnce
    {
        get { return toBeSetOnce; }
        set { toBeSetOnce.Value = value; }
    }
}
公共类Foo
{
private readonly SetOnce tobestonce=new SetOnce();
托贝斯顿公共酒店
{
获取{return tobestonce;}
设置{tobestonce.Value=Value;}
}
}
下面是更稳健的实施

public class SetOnce<T>
{
    private readonly object syncLock = new object();
    private readonly bool throwIfNotSet;
    private readonly string valueName;
    private bool set;
    private T value;

    public SetOnce(string valueName)
    {
        this.valueName = valueName;
        throwIfGet = true;
    }

    public SetOnce(string valueName, T defaultValue)
    {
        this.valueName = valueName;
        value = defaultValue;
    }

    public T Value
    {
        get
        {
            lock (syncLock)
            {
                if (!set && throwIfNotSet) throw new ValueNotSetException(valueName);
                return value;
            }
        }
        set
        {
            lock (syncLock)
            {
                if (set) throw new AlreadySetException(valueName, value);
                set = true;
                this.value = value;
            }
        }
    }

    public static implicit operator T(SetOnce<T> toConvert)
    {
        return toConvert.value;
    }
}


public class NamedValueException : InvalidOperationException
{
    private readonly string valueName;

    public NamedValueException(string valueName, string messageFormat)
        : base(string.Format(messageFormat, valueName))
    {
        this.valueName = valueName;
    }

    public string ValueName
    {
        get { return valueName; }
    }
}

public class AlreadySetException : NamedValueException
{
    private const string MESSAGE = "The value \"{0}\" has already been set.";

    public AlreadySetException(string valueName)
        : base(valueName, MESSAGE)
    {
    }
}

public class ValueNotSetException : NamedValueException
{
    private const string MESSAGE = "The value \"{0}\" has not yet been set.";

    public ValueNotSetException(string valueName)
        : base(valueName, MESSAGE)
    {
    }
}
public类SetOnce
{
私有只读对象syncLock=新对象();
私有只读布尔throwIfNotSet;
私有只读字符串valueName;
私有布尔集;
私人T值;
public SetOnce(字符串值名称)
{
this.valueName=valueName;
throwIfGet=true;
}
public SetOnce(字符串valueName,T defaultValue)
{
this.valueName=valueName;
值=默认值;
}
公共价值
{
得到
{
锁定(同步锁定)
{
如果(!set&&throwIfNotSet)抛出新的ValueNotSetException(valueName);
返回值;
}
}
设置
{
锁定(同步锁定)
{
如果(设置)抛出新的AlreadySetException(valueName,value);
设置=真;
这个值=值;
}
}
}
公共静态隐式运算符T(SetOnce toConvert)
{
返回convert.value;
}
}
公共类名称ValueException:InvalidOperationException
{
私有只读字符串valueName;
public-NamedValueException(string-valueName,string-messageFormat)
:base(string.Format(messageFormat,valueName))
{
this.valueName=valueName;
}
公共字符串值名称
{
获取{return valueName;}
}
}
公共类AlreadySetException:NamedValueException
{
private const string MESSAGE=“已设置值\“{0}\”;
公共AlreadySetException(字符串值名称)
:base(值名称、消息)
{
}
}
公共类ValueNotSetException:NamedValueException
{
private const string MESSAGE=“尚未设置值\“{0}\”;
public ValueNotSetException(字符串值名称)
:base(值名称、消息)
{
}
}

C#(从3.5开始)中没有此类功能。您必须自己编写代码。

这可以通过摆弄标志来完成:

private OneShot<int> setOnce;
private bool setOnceSet;

public OneShot<int> SetOnce
{
    get { return setOnce; }
    set
    {
        if(setOnceSet)
            throw new InvalidOperationException();

        setOnce = value;
        setOnceSet = true;
    }
}
private-OneShot-setOnce;
私人布尔-塞顿塞特;
公共一次性设置一次
{
获取{return setOnce;}
设置
{
if(setOnceSet)
抛出新的InvalidOperationException();
setOnce=值;
setOnceSet=true;
}
}
这是不好的,因为您可能会收到运行时错误。最好在编译时强制执行此行为:

public class Foo
{
    private readonly OneShot<int> setOnce;        

    public OneShot<int> SetOnce
    {
        get { return setOnce; }
    }

    public Foo() :
        this(null)
    {
    }

    public Foo(OneShot<int> setOnce)
    {
        this.setOnce = setOnce;
    }
}
公共类Foo
{
私有只读一次性设置一次;
公共一次性设置一次
{
获取{return setOnce;}
}
public Foo():
此(空)
{
}
公共Foo(一次性设置一次)
{
this.setOnce=setOnce;
}
}

然后使用任意一个构造函数。

正如Marc所说,在.Net中默认情况下没有办法做到这一点,但是自己添加一个并不太困难

public class SetOnceValue<T> { 
  private T m_value;
  private bool m_isSet;
  public bool IsSet { get { return m_isSet; }}
  public T Value { get {
    if ( !IsSet ) {
       throw new InvalidOperationException("Value not set");
    }
    return m_value;
  }
  public T ValueOrDefault { get { return m_isSet ? m_value : default(T); }}
  public SetOnceValue() { }
  public void SetValue(T value) {
    if ( IsSet ) {
      throw new InvalidOperationException("Already set");
    }
    m_value = value;
    m_isSet = true;
  }
}
public类SetOnceValue{
私人T m_值;
私人住宅;
公共bool IsSet{get{return m_IsSet;}
公共T值{get{
如果(!IsSet){
抛出新的InvalidOperationException(“未设置值”);
}
返回m_值;
}
公共T值默认值{get{返回m_isSet?m_值:默认值(T);}
public SetOnceValue(){}
公共无效设置值(T值){
如果(IsSet){
抛出新的InvalidOperationException(“已设置”);
}
m_值=值;
m_isSet=真;
}
}
然后,您可以将其用作特定属性的支持。

您可以这样做,但我
public class SetOnceValue<T> { 
  private T m_value;
  private bool m_isSet;
  public bool IsSet { get { return m_isSet; }}
  public T Value { get {
    if ( !IsSet ) {
       throw new InvalidOperationException("Value not set");
    }
    return m_value;
  }
  public T ValueOrDefault { get { return m_isSet ? m_value : default(T); }}
  public SetOnceValue() { }
  public void SetValue(T value) {
    if ( IsSet ) {
      throw new InvalidOperationException("Already set");
    }
    m_value = value;
    m_isSet = true;
  }
}
interface IFoo {

    int Bar { get; }
}

class Foo : IFoo {

    public int Bar { get; set; }
}

class Program {

    public static void Main() {

        IFoo myFoo = new Foo() {
            Bar = 5 // valid
        };

        int five = myFoo.Bar; // valid

        myFoo.Bar = 6; // compilation error
    }
}
/// <summary>
/// Wrapper for once inizialization
/// </summary>
public class WriteOnce<T>
{
    private T _value;
    private Int32 _hasValue;

    public T Value
    {
        get { return _value; }
        set
        {
            if (Interlocked.CompareExchange(ref _hasValue, 1, 0) == 0)
                _value = value;
            else
                throw new Exception(String.Format("You can't inizialize class instance {0} twice", typeof(WriteOnce<T>)));
        }
    }

    public WriteOnce(T defaultValue)
    {
        _value = defaultValue;
    }

    public static implicit operator T(WriteOnce<T> value)
    {
        return value.Value;
    }
}
public class ReadOnly<T> // or WriteOnce<T> or whatever name floats your boat
{
    private readonly TaskCompletionSource<T> _tcs = new TaskCompletionSource<T>();

    public Task<T> ValueAsync => _tcs.Task;
    public T Value => _tcs.Task.Result;

    public bool TrySetInitialValue(T value)
    {
        try
        {
            _tcs.SetResult(value);
            return true;
        }
        catch (InvalidOperationException)
        {
            return false;
        }
    }

    public void SetInitialValue(T value)
    {
        if (!TrySetInitialValue(value))
            throw new InvalidOperationException("The value has already been set.");
    }

    public static implicit operator T(ReadOnly<T> readOnly) => readOnly.Value;
    public static implicit operator Task<T>(ReadOnly<T> readOnly) => readOnly.ValueAsync;
}
public class CreatedAtPointA 
{
    public int ExamplePropOne { get; }
    public bool ExamplePropTwo { get; }

    public CreatedAtPointA(int examplePropOne, bool examplePropTwo)
    {
        ExamplePropOne = examplePropOne;
        ExamplePropTwo = examplePropTwo;
    }
}

public class CreatedAtPointB : CreatedAtPointA
{
    public string ExamplePropThree { get; }

    public CreatedAtPointB(CreatedAtPointA dataFromPointA, string examplePropThree) 
        : base(dataFromPointA.ExamplePropOne, dataFromPointA.ExamplePropTwo)
    {
        ExamplePropThree = examplePropThree;
    }
}
public class SetOnce<T>
{
    bool set;
    T value;

    public SetOnce(T init) =>
        this.value = init;

    public T Value
    {
        get => this.value;
        set
        {
            if (this.set) throw new AlreadySetException($"Not permitted to override {this.Value}.");
            this.set = true;
            this.value = value;
        }
    }

    public static implicit operator T(SetOnce<T> setOnce) =>
        setOnce.value;

    class AlreadySetException : Exception
    {
        public AlreadySetException(string message) : base(message){}
    }
}
public DateTime RecordedAt { get; init; }