【问题标题】:variable that can't be modified无法修改的变量
【发布时间】:2011-04-09 02:04:50
【问题描述】:

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

【问题讨论】:

  • 语言吹毛求疵:“无法修改的变量”是矛盾的。

标签: c# .net variables constants readonly


【解决方案1】:

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

【讨论】:

  • 所以它必须来自构造函数?
  • 你可以限制访问并且只允许在你的类中设置字段,但是readonlyconst是唯一允许在构造或声明之后设置值的语言结构。
  • 是的,无论是它的构造函数还是声明中的内联(但就像一个 const 那样)。此外,它必须在构造函数中,而不仅仅是在构造时 - 这意味着不可能在构造函数调用的函数中设置只读变量的值。
【解决方案2】:

当然。你可以使用readonly:

即:public readonly int z;

这只能在构造函数中修改。

来自MSDN

您只能在以下情况下为只读字段赋值:

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

  • public readonly int y = 5;

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

但是,如果您想要创建一个只能在创建它的类中更改的属性,您可以使用以下内容:

public string SetInClass
{
   get;
   private set;
}

这允许在类内进行更改,但变量不能从类外更改。

【讨论】:

    【解决方案3】:

    您可以定义一个只读变量,该变量只能在对象构造函数中设置其值。

    在这里阅读:http://msdn.microsoft.com/en-us/library/acdd6hb7%28v=VS.100%29.aspx

    【讨论】:

      【解决方案4】:

      可以标记一个字段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;
            }
        }
        

        编辑:

        这不会阻止类内部更改私有字段。

        【讨论】:

        • @Kyle,为什么?如果您查看上面的编辑,它说它可能不会从排除只读的构造函数中设置!考虑到 OP 的要求,对我来说似乎是一个有效的实现!
        • @Louis,正如 Kyle 和 OneSHOT 所说,没有什么可以阻止该类多次更改值,因为它可以直接访问私有变量。
        • 已删除评论,没有看到编辑 - 不错,只要私有变量不能设置回 null。
        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

          您可以创建自己的通用类来提供此功能,但这可能有点过头了。

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

          【讨论】:

          • @Louis,已更新,它应该是语法正确的,但我确实直接作为代码块输入作为答案的一部分 - 我不推荐 SO 作为代码编辑器 - 没有源代码控制集成一件事:)
          • 为了公平起见,我接受这一点,因为考虑到我的问题中的要求,这是有效的..即使实际上我更喜欢 ck 的回答,这实际上是我需要的
          【解决方案8】:

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

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

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

          对只读变量有一定的限制,有助于确保 C# 的正常操作不会引入突变。这可能会导致一些意想不到的结果!见

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

          了解详情。

          一些本地人实际上也是只读的。例如,当您说using(Stream s = whatever) 时,在using 的嵌入语句中您不能更改s 的值。此限制的原因是为了防止您创建要释放的资源,然后在释放变量 s 的内容时释放 不同 资源的错误。最好是一样的。

          (不幸的是,在 C# 中存在一些错误,涉及以下情况:已处置的资源是结构类型,结构具有改变结构的方法,局部变量是或不是匿名函数的封闭局部或迭代器块;由于场景晦涩难懂,而且修复可能会中断,我们还没有做任何事情,有待进一步分析。)

          foreach 语句中声明的局部变量实际上也是readonly——该变量每次循环都会改变值,但不允许改变它的值。 p>

          没有办法使只读形式参数、数组元素或指针解引用。

          有多种方法可以“打破”只读限制并写入应该是只读的变量。如果您有足够的权限,您可以使用反射或不安全代码来打破 CLR 的几乎所有安全限制。如果你这样做会很痛,那就不要那样做;有了这些权力,您就有责任知道自己在做什么并正确行事。

          【讨论】:

          • +1 用于解释 C# 的内部结构。非常有教育意义!
          • "只有字段声明可以是只读的;C# 不支持用户声明的只读本地变量。"尽管原始、字符串、枚举和 ValueType 派生的局部变量可以声明为 const,这对于这些类型实际上是相同的(尽管可以说它更接近于文字别名而不是只读的局部变量)。
          • @Paul:当然这些不是变量。常量不是变量,因为 constantvariable相反的。 (而且你说 ValueType 派生的局部变量可以是常量是不正确的。我向你保证他们不能。我怀疑你正在考虑其他语言;也许是 C++。)
          • 我只是指出在某些情况下可以将变量交换为 const 以获得类似于 OP 所要求的行为。在 ValueType 方面很奇怪,我可以发誓我以前做过(也许我做过,但它没有编译!)。
          【解决方案9】:

          由于@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#。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-03-22
            相关资源
            最近更新 更多