【发布时间】:2010-11-25 16:50:00
【问题描述】:
有没有办法让类中的私有只读字段可以在类中的任何位置赋值,但只能赋值一次??
也就是说,我正在寻找一种私有只读类型的字段,它只能被赋值一次,但不一定在构造函数中。因此,如果一个值被重新分配给一个字段,那么它会显示编译时错误(我确信这要求太多了)。
如果有任何模式(不是语言功能)可以做同样的工作,我也很想知道。
感谢您的关注。
【问题讨论】:
有没有办法让类中的私有只读字段可以在类中的任何位置赋值,但只能赋值一次??
也就是说,我正在寻找一种私有只读类型的字段,它只能被赋值一次,但不一定在构造函数中。因此,如果一个值被重新分配给一个字段,那么它会显示编译时错误(我确信这要求太多了)。
如果有任何模式(不是语言功能)可以做同样的工作,我也很想知道。
感谢您的关注。
【问题讨论】:
您不会收到编译时错误,但您可以使用属性完成您要查找的内容。在 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;
}
}
【讨论】:
简短的回答是否定的。唯一可以进行编译器检查的一次性赋值的地方是在构造函数中。您可以创建一个系统,如果多次尝试分配但没有 C# 构造来执行此操作,则会出现运行时错误
【讨论】:
您可以创建一个通用结构来自动化该过程:
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; }
}
但是,这仍然没有真正解决我在原始 sn-p 中犯的最后一个严重错误,它创建了一个可变结构。如果您正在做很多这样的事情,可能为了不重复自己而违反该原则,但这绝对是一个潜在的陷阱,需要仔细评论并远离任何公共接口。
但是,如果它只是一次性的,那么其他人所建议的关于直接实现属性的建议就不那么复杂和安全了。
【讨论】:
您可以使用私有属性来检查它是否已分配,如果尚未分配,则仅分配一个值。
int? _writeOnceField;
private int? WriteOnce
{
set
{
if (!_writeOnceFiled.HasValue)
{
writeOnceFiled = value;
}
else
{
// Throw exception
}
}
}
【讨论】:
没有对此的语言支持,但您可以创建一个 setter 来实现所需的行为。显然,这不会给您带来编译时错误,但如果没有可能是您能做的最好的语言支持。
【讨论】:
这看起来很适合声明分配或单例。
public class SomeClass {
private readonly Type t = typeof(SomeClass);
}
否则,就像其他人所说的那样,只使用一个 setter 来创建一个内部或 so 属性,然后为其分配一个值。
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
}
//(...)
}
【讨论】: