【发布时间】:2011-04-09 02:04:50
【问题描述】:
C# 是否允许不能修改的变量?它就像const,但不必在声明时为其赋值,变量没有任何默认值,但只能在运行时赋值一次(编辑:可能不是来自构造函数)。还是这不可能?
【问题讨论】:
-
语言吹毛求疵:“无法修改的变量”是矛盾的。
标签: c# .net variables constants readonly
C# 是否允许不能修改的变量?它就像const,但不必在声明时为其赋值,变量没有任何默认值,但只能在运行时赋值一次(编辑:可能不是来自构造函数)。还是这不可能?
【问题讨论】:
标签: c# .net variables constants readonly
您可以声明一个readonly field变量,该变量只能在构造函数中设置或直接通过其声明设置。
【讨论】:
readonly和const是唯一允许在构造或声明之后设置值的语言结构。
当然。你可以使用readonly:
即:public readonly int z;
这只能在构造函数中修改。
来自MSDN:
您只能在以下情况下为只读字段赋值:
在声明中初始化变量时,例如:
public readonly int y = 5;
对于实例字段,在包含字段声明的类的实例构造函数中,或者对于静态字段,在包含字段声明的类的静态构造函数中。这些也是唯一可以将只读字段作为out 或ref 参数传递的上下文。
但是,如果您想要创建一个只能在创建它的类中更改的属性,您可以使用以下内容:
public string SetInClass
{
get;
private set;
}
这允许在类内进行更改,但变量不能从类外更改。
【讨论】:
您可以定义一个只读变量,该变量只能在对象构造函数中设置其值。
在这里阅读:http://msdn.microsoft.com/en-us/library/acdd6hb7%28v=VS.100%29.aspx
【讨论】:
可以标记一个字段readonly,这将要求您在声明点或构造函数中为其设置一个值,然后防止在构造后重新分配它。
然而,虽然引用是只读的,但对象不一定也是如此。为了防止对象本身被修改,您必须使类型不可变,或者提供一个仅公开底层类型的非破坏性方法和属性的包装类。
【讨论】:
您可以使用自定义设置器自己滚动(但不要使用Object,除非您必须选择正确的类):
private Object myObj = null;
private Boolean myObjSet = false;
public Object MyObj
{
get { return this.myObj; }
set
{
if (this.myObjSet) throw new InvalidOperationException("This value is read only");
this.myObj = value;
this.myObjSet = true;
}
}
编辑:
这不会阻止类内部更改私有字段。
【讨论】:
如果您想在构建包含它的对象之后在运行时分配该变量,您可以使用一个自定义属性和一个只能修改一次的 setter 方法。即。
private myVariable = null;
public object MyVariable
{
get { return myVariable; }
set { if(myVariable == null ) myVariable = value;
}
【讨论】:
您可以创建自己的通用类来提供此功能,但这可能有点过头了。
public class SetValueOnce<T>
{
public bool _set;
private T _value;
public SetValueOnce()
{
_value = default(T);
_set = false;
}
public SetValueOnce(T value)
{
_value = value;
_set = true;
}
public T Value
{
get
{
if(!_set)
throw new Exception("Value has not been set yet!");
return _value;
{
set
{
if(_set)
throw new Exception("Value already set!");
_value = value;
_set = true;
}
}
}
【讨论】:
是的,在 C# 中有几种方法可以做到这一点。
首先,什么是“变量”?变量是一个存储位置。局部变量、方法的形式参数(以及索引器、构造器等)、静态和实例字段、数组元素和指针取消引用都是变量。
有些变量可以声明为“只读”。 “只读”变量只能通过声明中的初始化程序或在构造函数中更改一次。只有字段声明可以是只读的; C# 不支持用户声明的只读本地变量。
对只读变量有一定的限制,有助于确保 C# 的正常操作不会引入突变。这可能会导致一些意想不到的结果!见
http://ericlippert.com/2008/05/14/mutating-readonly-structs/
了解详情。
一些本地人实际上也是只读的。例如,当您说using(Stream s = whatever) 时,在using 的嵌入语句中您不能更改s 的值。此限制的原因是为了防止您创建要释放的资源,然后在释放变量 s 的内容时释放 不同 资源的错误。最好是一样的。
(不幸的是,在 C# 中存在一些错误,涉及以下情况:已处置的资源是结构类型,结构具有改变结构的方法,局部变量是或不是匿名函数的封闭局部或迭代器块;由于场景晦涩难懂,而且修复可能会中断,我们还没有做任何事情,有待进一步分析。)
在foreach 语句中声明的局部变量实际上也是readonly——该变量每次循环都会改变值,但你不允许改变它的值。 p>
没有办法使只读形式参数、数组元素或指针解引用。
有多种方法可以“打破”只读限制并写入应该是只读的变量。如果您有足够的权限,您可以使用反射或不安全代码来打破 CLR 的几乎所有安全限制。如果你这样做会很痛,那就不要那样做;有了这些权力,您就有责任知道自己在做什么并正确行事。
【讨论】:
由于@Ivan 最近询问了similar question,让我建议另一种方法来实例化属性在代码中的任何位置,当然,更准确地说,在支持下泛型构造函数。
public class Immutable<T> {
public T Val { get; private set; }
public Immutable(T t) {
Val = t;
}
}
用法是
var immutableInt1 = new Immutable<int>(3); // you can set only once
immutableInt1.Val = 5; // compile error here: set inaccessible
Console.WriteLine("value: " + immutableInt1.Val);
关键是现在如果你尝试设置一个新值,你会得到一个编译错误。
除此之外,如果你想遵循这种范式,我相信你最好使用 F# 之类的函数式语言而不是 C#。
【讨论】: