C# 可以在构造函数外部赋值的只读字段

C# 可以在构造函数外部赋值的只读字段,c#,readonly,C#,Readonly,是否有一种方法可以在类中的任何位置为私有只读字段赋值,但只能赋值一次 也就是说,我正在寻找一种私有的只读类型的字段,它只能被赋值一次,但不一定在构造函数内部。所以,如果一个值被重新分配给一个字段,那么它将显示编译时错误(我确信这要求太多) 如果有任何模式(不是语言特性)可以做同样的工作,我也会很有兴趣知道 感谢您的关注。您不会遇到编译时错误,但您可以使用属性完成所需的操作。在setter中,仅当内部bool/标志为false时才设置该值。第一次设置后,将标志设置为true。如果另一段代码再次调用

是否有一种方法可以在类中的任何位置为私有只读字段赋值,但只能赋值一次

也就是说,我正在寻找一种私有的只读类型的字段,它只能被赋值一次,但不一定在构造函数内部。所以,如果一个值被重新分配给一个字段,那么它将显示编译时错误(我确信这要求太多)

如果有任何模式(不是语言特性)可以做同样的工作,我也会很有兴趣知道


感谢您的关注。

您不会遇到编译时错误,但您可以使用属性完成所需的操作。在setter中,仅当内部bool/标志为false时才设置该值。第一次设置后,将标志设置为true。如果另一段代码再次调用setter,您可以抛出InvalidOperationException,让他们知道它已经初始化

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;
   }
}

您可以使用私有属性来检查它是否已指定,并且仅在未指定值时才指定值

int? _writeOnceField;

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

没有语言支持,但是您可以创建一个setter,它实现了所需的行为。显然,这不会给您带来编译时错误,但是如果没有语言支持,这可能是您所能做的最好的了

简短的回答是否定的。编译器检查的一次性赋值只能在构造函数中进行。您可以创建一个系统,如果多次尝试分配,但没有C#construct来执行此操作,则会出现运行时错误

您可以创建一个通用结构来自动执行此过程:

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

    public T Value
    {
        get { return _value; }
        set
        {
            if (_value.HasValue) throw new InvalidOperationException();
            _value = value;
        }
    }
}
公共结构写入
{
私人T?u值;
公共价值
{
获取{返回_值;}
设置
{
如果(_value.HasValue)抛出新的InvalidOperationException();
_价值=价值;
}
}
}
编辑 我刚刚意识到上面的内容甚至无法编译。编译器不允许_值的类型为Nullable,因为Nullable中的t必须是非Nullable类型

下面是一个更好的实现:

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;
    }
}
公共结构写入
{
私人T_值;
私有布尔值;
公共bool HasValue{get{return\u HasValue;}}
公共价值
{
获取{返回_值;}
设置
{
if(HasValue)抛出新的InvalidOperationException();
_价值=价值;
_hasValue=true;
}
}
公共静态隐式运算符T(WriteOnce x)
{
返回x.值;
}
公开书写(T val)
{
_值=val;
_hasValue=true;
}
}
注意,我说它会更好——不是说它会很好地工作。在使用它时仍然存在一些问题:

首先,如果编译器检测到您试图使用它而没有首先为它分配某些内容,它会抱怨。像这样初始化它将解决以下问题:

WriteOnce<int> foo = default(WriteOnce<int>);
WriteOnce foo=默认值(WriteOnce);
其次,即使它在修改时抛出异常,C#仍然会很高兴地让您覆盖它。因此,虽然这确实封装了一些功能;如果它最终暴露在对象的接口中,您仍然应该将其包装在属性中以防止误用

private WriteOnce<int> _someInt = default(WriteOnce<int>);
public int SomeInt
{ 
    get { return _someInt; }
    set { _someInt.Value = value; }
}
private WriteOnce\u someInt=default(WriteOnce);
公共整数
{ 
获取{return\u someInt;}
设置{u someInt.Value=Value;}
}
然而,这仍然没有真正绕过我在原始代码片段中犯的最后一个严重错误,即创建可变结构。如果你正在做很多类似的事情,为了不重复你自己,违反这一原则可能是值得的,但这肯定是一个潜在的陷阱,需要仔细评论,并将其排除在任何公共接口之外


但是,如果只是一次性的,那么其他人建议直接实现一个属性就不那么复杂和安全了。

这看起来是声明赋值或单例的好选择

public class SomeClass {
    private readonly Type t = typeof(SomeClass);
}
或者,像其他人所说的那样,只使用setter创建一个内部属性,然后为其赋值

public class SomeClass {
    private Type t;

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

我想我找到了一种方法,如果它是在构造函数中完成的。它也可以位于变量声明行上。 如果在这些上下文之外执行,您将收到一个错误:无法将只读字段分配给(构造函数或变量初始值设定项中除外)


很抱歉发布此消息,我没有注意标题中的“外部构造函数”部分。
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
    }

    //(...)
}