可以在构造函数外部分配值的只读字段

时间:2010-11-25 16:50:00

标签: c# readonly

有没有办法让类中的私有只读字段可以在类中的任何位置分配值,但只有一次?

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

如果有任何模式(不是语言特征)可以做同样的工作,那么真的有兴趣了解它。

感谢您的关注。

7 个答案:

答案 0 :(得分:4)

您不会遇到编译时错误,但您可以使用属性完成您要查找的内容。在setter中,仅在内部bool / flag为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;
   }
}

答案 1 :(得分:1)

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

答案 2 :(得分:1)

您可以创建一个通用结构来自动化该过程:

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

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

修改 我刚刚意识到上面甚至不会编译。编译器不允许_value的类型为Nullable,因为Nullable中的T必须是非可空类型。

这是一个可以更好地实现的实现:

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

注意我说它会更好 - 而不是它会运作良好。使用它还有一些问题:

首先,如果编译器检测到您正在尝试使用它而不首先为其分配内容,则会抱怨。像这样初始化它将照顾到:

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

其次,即使它在修改时抛出异常,C#仍然会很乐意让你覆盖它。所以虽然这确实封装了一些功能;你仍然应该将它包装在一个属性中,以防止误用,如果它最终暴露在对象的界面中。

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

然而,这仍然没有真正解决我在原始片段中提交的最后一个令人震惊的错误,该错误创建了一个可变结构。如果你做了很多像这样的事情,可能值得违反这个原则,为了不重复自己,但这绝对是一个潜在的陷阱,需要仔细评论并保持不变任何公共接口。

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

答案 3 :(得分:0)

您可以使用私有属性检查是否已分配给它,如果尚未分配值,则可以使用它。

int? _writeOnceField;

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

这看起来像是声明作业或单身人士的好候选人。

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

答案 6 :(得分:0)

如果在构造函数中完成,我认为我已经实现了一种方法。它也可以在变量声明行上。 如果在这些上下文之外完成,您将收到错误:无法分配只读字段(构造函数或变量初始化程序除外)

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
    }

    //(...)
}