2010-09-07 136 views
12

C#是否允许一个无法修改的变量?它就像一个const,但不必在声明中为它赋值,该变量没有任何默认值,但只能在运行时(编辑:可能不是来自构造函数)赋值一次。或者这是不可能的?不能修改的变量

+12

语言挑剔:“一个不能修改的变量”是一个矛盾。 – LukeH 2010-09-07 08:49:38

回答

5

您可以创建自己的泛型类,提供此功能,但这可能是矫枉过正。

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

你忘了一些引号 – 2010-09-07 09:04:34

+0

@Louis,已更新,它应该是语法正确的,但我直接键入作为代码块作为答案的一部分 - 我不会推荐SO一个代码编辑器 - 没有源代码控制集成的一件事:) – 2010-09-07 09:07:40

+0

为了公平我认为这是因为这是有效的考虑到我的问题的要求..即使其实我更喜欢ck的答案,它实际上是我需要的 – 2010-09-08 02:22:19

12

您可以声明一个readonly变量,该变量只能在构造函数中或直接通过其声明设置。

+0

所以它必须来自构造函数? – 2010-09-07 08:20:39

+0

你可以限制访问,只允许在你的类中设置字段,但是'readonly'和'const'是唯一允许在构造或声明后设置值的语言结构。 – 2010-09-07 08:27:31

+1

是的,无论是它的构造函数还是在声明中内联(但就像const那样)。它也必须在构造函数中,而不仅仅是在构造时 - 这意味着不可能在构造函数调用的函数中设置只读变量的值。 – 2010-09-07 08:29:17

4

当然。您可以使用readonly

即:public readonly int z;

这只能从构造方法中修改。

MSDN

只能在以下上下文中的值赋给readonly字段:

当变量在声明中初始化,例如:

  • public readonly int y = 5;

  • 对于实例字段,在类t的实例构造函数中在包含字段声明的类的静态构造函数中,hat包含字段声明或静态字段。这些也是唯一可以将只读字段作为outref参数传递的环境。

然而,如果你想要做只能创建它的类内改变的属性,可以使用以下命令:

public string SetInClass 
{ 
    get; 
    private set; 
} 

这样就可以在类中所做的更改,但变量不能从以外的班级改变。

1

这是可以标记一个字段readonly这将需要你设置它在声明的或在构造函数点的值或者,然后将防止它被重新分配岗位建设。

但是,虽然引用是只读的,但对象不一定会是这样。为了防止对象本身被修改,你将不得不使类型不可变或者提供一个只能公开基础类型的非破坏性方法和属性的包装类。

5

您可以使用自定义的制定者推出自己的(但不使用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; 
    } 
} 

编辑:

这没有按”不要停止正在被类内部改变的私有字段。

+1

@凯尔,为什么?如果你看看上面的编辑,它说它可能不会从构造函数中设置,它排除只读!看起来对我来说是一个有效的实现给予OP的要求! – OneSHOT 2010-09-07 08:32:10

+0

哇好主意! – 2010-09-07 08:35:32

+2

@Louis,正如Kyle和OneSHOT所说,没有什么能够阻止班级改变数值的次数,因为它可以直接访问私有变量。 – 2010-09-07 08:37:08

0

如果你想在包含它的对象被创建后在运行时分配变量,你可以使用一个自定义属性和一个只能被修改一次的setter方法。即。

private myVariable = null; 
public object MyVariable 
{ 
    get { return myVariable; } 
    set { if(myVariable == null) myVariable = value; 
} 
23

是的,在C#中有几种方法可以做到这一点。

首先,什么是“变量”?变量是存储位置。局部变量,方法(以及索引器,构造函数等)的形式参数,静态和实例字段,数组元素和指针取消引用都是变量。

某些变量可以声明为“只读”。 “只读”变量只能由声明中的初始化程序或构造函数更改一次。只有字段声明可以只读; C#不支持用户声明的只读本地人。

readonly变量有一定的限制,有助于确保C#的正常运行不会引入变化。这可能会导致一些意想不到的结果!见

http://ericlippert.com/2008/05/14/mutating-readonly-structs/

了解详情。

一些当地人也是有效的只读。例如,当你说using(Stream s = whatever)然后在using的嵌入语句中,你不能改变s的值。此限制的原因是为了防止创建要处置的资源的错误,并且在处理变量s的内容时处置不同的资源。最好是一样的。

(不幸的是,在C#中存在一些错误,涉及处置资源是结构类型的情况,结构具有改变结构的方法,并且局部变量是或者不是匿名函数的闭合局部或迭代器块;由于场景模糊,修复可能会破坏,我们还没有做任何有关它的事情,等待进一步分析。)

foreach语句中声明的局部变量也是有效的readonly每次通过循环变量更改值,但不允许更改其值。

没有办法使只读的形式参数,数组元素或指针取消引用。

有许多方法可以“破坏”只读限制,并写入一个应该只读的变量。如果您有足够的权限来执行此操作,则可以使用反射或不安全代码来突破CLR的任何安全限制。如果这样做会伤害你,不要这样做;与这些权力有责任知道你在做什么,做对了。

+1

+1为解释更多的C#的内部..非常有教育意义! – Arcturus 2010-09-07 15:27:55

+0

“只有字段声明可以只读; C#不支持用户声明的只读本地语言。”虽然原始,字符串,枚举和ValueType派生的本地可以被声明为* const *,这对于这些类型实际上是相同的东西(尽管可以说它比只读本地变量更接近文字别名)。 – 2010-09-07 15:28:09

+0

@Paul:但当然这些不是*变量*。常量不是变量,因为*常量*和*变量*是*对立*。 (你说错的是ValueType派生的本地变量可以是常量,我向你保证它们不能,我怀疑你正在考虑其他的语言,也许C++) – 2010-09-07 15:36:41

1

由于similar question已经@Ivan最近问,让我提出另一种方法在任何地方在代码来实例化一个属性,当然,更确切地说,与支持通用构造。

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#。