【问题标题】:Why can't properties be readonly?为什么属性不能是只读的?
【发布时间】:2010-01-30 03:48:46
【问题描述】:

这个问题出现在this answer 的cmets 中。无法拥有只读属性被提议作为使用字段而不是属性的潜在原因。

例如:

class Rectangle
{
   private readonly int _width;
   private readonly int _height;

   public Rectangle(int width, int height)
   {
      _width = width;
      _height = height;
   }

   public int Width { get { return _width; } }
   public int Height { get { return _height; } }
}

但是你为什么不能这样做呢?

public int Width { get; readonly set; }

编辑(澄清):您可以在第一个示例中实现此功能。但是为什么你不能使用自动实现的属性速记来做同样的事情呢?它也不会那么混乱,因为您不必直接访问构造函数中的字段;所有访问都将通过该物业进行。

编辑(更新):从 C# 6.0 开始,支持只读属性! object MyProp { get; } 该属性可以内联 (object MyProp { get; } = ...) 或在构造函数中设置,但不能在其他任何地方设置(就像 readonly 字段一样)。

【问题讨论】:

  • 更好的语法是public int Width { get; readonly set; }
  • @Jason:看起来确实更好——我已将其编辑为您的版本。
  • @Jason:我不知道,“只读设置器”的概念对我来说似乎很奇怪。在这个问题的背景下,在这里很容易理解,但我认为如果我突然看到它,那将是一个 WTF 时刻。
  • @Aaronaught:我确定您知道关键字 readonly 的含义,但我们很清楚,这意味着直接分配给标有 readonly 修饰符的变量只能发生在声明点或构造函数中。因此,readonly set 将意味着“此属性只能在构造函数或对象初始化程序中设置。”
  • 注意:Jon Skeet 在他的 DDD8 广播中特别提到了这一点,作为他在 C# 5.0 中想要的东西。

标签: c# properties


【解决方案1】:

因为语言不允许。

这似乎是一个轻率的答案:毕竟,语言设计者可以声明如果您在自动属性上使用readonly,那么这将意味着“该属性是可设置的,但只能在构造函数”。

但功能并非免费提供。 (Eric Gunnerson 将其表示为“每个功能都以 minus 100 points 开头。”)要实现只读自动属性,需要额外的编译器工作来支持属性上的 readonly 修饰符(它目前仅适用于字段),以生成适当的支持字段并将属性的sets 转换为支持字段的分配。要支持用户可以通过声明只读支持字段并编写单行属性获取器来合理轻松地完成的事情,这是一项相当多的工作,而且这项工作会因不实现其他功能而付出代价。

因此,非常严肃地说,答案是语言设计者和实现者要么从未想过这个想法,要么 - 更有可能 - 他们认为拥有它会很好,但决定有更好的地方来度过他们的有限的资源。没有技术限制阻止语言设计者和实现者提供您建议的功能:原因更多是关于软件开发的经济性。

【讨论】:

  • 是 Eric Gunnerson 说的:blogs.msdn.com/ericgu/archive/2004/01/12/57985.aspx。否则,很棒的帖子和正确的答案。
  • 是后者。从 C# 3 开始,这一直在列表中。我们很乐意这样做,但它从来都不是一个足够高的优先级。我们为新的语言功能提出了数百个想法,而每个版本中我们只能做几个。
  • @EricLippert 请问以后有没有机会支持它?
  • @ken2k:你可能会问,但你不会得到一个好的答案。对于 StackOverflow 来说,需要预测未来的问题是不好的问题。概率不为零,但你已经知道了。
  • 从 C# 6.0 开始,支持只读属性! object MyProp { get; } 这个属性可以内联(object MyProp { get; } = ...)或在构造函数中设置,但不能在其他地方设置(就像只读字段一样)。
【解决方案2】:

如果您想将属性设为“只读”,就功能而言,您只需提供 get 方法即可,正如您在帖子中指出的那样。

public int Width { get { return _width; } } 
public int Height { get { return _height; } } 

如果您尝试写入它们,编译器甚至会将它们引用为“只读”。

为属性添加一个附加术语 readonly 会与同时提供 set 方法发生冲突。对我来说,它的语法似乎很糟糕,即阅读它的人(或编译器,就此而言)如何知道什么优先:readonlyset

此外,正如您在引用的答案中所解释的那样,readonly 仅适用于字段,并将对这些字段的写入限制为类的实例化。使用属性,即使在构造函数中,如果它们只有 get 方法,你也不能写入它们(我不认为)。

【讨论】:

  • 你是对的,它提供了相同的功能。这就是为什么它被包括在内作为一个例子。我的问题是:为什么你不能使用自动实现的属性速记来做同样的事情?它也不会那么混乱,因为您不必直接访问构造函数中的字段;所有访问都将通过该属性进行。
  • @Matthew 当然,也许负责 C# 编译器的团队可以走那条路,但这不会更令人困惑吗?正如@Nate 解释的那样,您可以拥有readonly 的属性,而不是自动属性,这是有道理的。虽然实现您所说的可能可能,但我确实认为这会令人困惑(我,一方面,会感到困惑)。
【解决方案3】:

您可以通过为 set 指定私有访问修饰符来使自动属性只读

public bool Property {get; private set;}

setter 仍被定义,但在定义属性的类之外不再可见。顺便说一句,有时将 setter 定义为 internal 很有用,这样可以在同一个程序集中轻松设置属性,但不能由外部调用者设置。

【讨论】:

  • 这是真的,但是,没有设置器或私有设置器的属性仍然会产生编译时错误并阻止值修改,所以除非我缺少只读的东西并且没有一个 setter 的功能是等效的
  • 它不会阻止从构造函数外部修改类内部的值。
  • @Crippledsmurf:你说得对,这是“只读”的典型定义,但 C# 中的 readonly 比这更具体一点。这意味着一个字段只能设置一次,并且只能在构造函数中(或通过内联初始化)。
【解决方案4】:

属性可以是只读的,而不是自动属性。

getset 都是自动属性所必需的,并且只读属性具有 set 是没有意义的。

您可以通过定义 get 将常规属性定义为只读属性 - 但是,即使不存在对自动属性的 getset 的要求 - 只读属性无法自动定义,因为您必须知道支持字段才能在内部(即通过构造函数)设置它的值。

我想可能有一个模板/宏或在 VS 中定义的东西来生成代码,但它不能是语言本身的一部分。

【讨论】:

  • 编译器允许具有只读自动属性的字段初始化器语法是完全合乎逻辑的,至少对于不实现IDisposable的字段。
【解决方案5】:

我认为从根本上说问题在于属性只是具有可选 getter/setter 方法的字段的语法糖。自动属性生成支持字段,因此它们需要“setter”,否则将无法设置支持字段的值。由于属性确实映射到方法而不是字段,因此将它们设为只读没有任何意义。

即使允许,只读也只能应用于自动属性。对于传统属性,您可以在 getter 和 setter 中放入任意代码。即使 setter 只能在类的构造函数中调用,getter 仍然可以根据您决定放入其中的任何逻辑来改变值。这将与 readonly 的概念完全不一致,因此需要不同的语法规则并支持自动/传统属性。由于有一种机制——使用传统的属性,只定义了一个 getter 和一个只读的支持字段,就像引用的问题一样——我认为没有必要混淆属性语法,并可能会为具有相当简单和直接的实现的东西引入混淆使用当前的语言结构。

【讨论】:

  • 非常好的一点 - “由于属性确实映射到方法,而不是字段,因此将它们设为只读没有任何意义。”
  • @tvanfosson,@Ben McCormack:这绝对是错误的。 public int Width { get; readonly set; } 映射到 readonly int _width; public int Width { get { return _width; } }Width = 17 仅在构造函数中合法有什么问题?
  • @Jason 也许我遗漏了一些东西,但我不明白你在说什么。我不相信他(或我)说您不应该设置缺少 set 方法的属性(因此在编译时使它们“只读”)。他是说 关键字 readonly 只能应用于字段,而不是方法(因此扩展属性)。
  • @Jason -- 同样,它仅适用于自动属性,因为如果您允许在 getter 中使用任意代码,则无法强制执行 readonly 的语义。这将使自动属性从根本上不同于传统属性的简写符号。 IMO 对于可以使用显式只读支持字段实现且在传统属性上没有设置器的极端情况是不值得的。
  • @Jason - 我们在争论不同的观点。我从来没有说过语言不能不同,或者改变不会让一些事情变得更容易。我是说这样做只会适用于自动属性,会使自动属性与传统属性在根本上有所不同,并且不会真正添加任何使用两行而不是一行的内容(或者如果在 API 中添加注释)将。我根本看不到足够的价值来对属性的处理方式进行如此根本的改变。我认为在这种情况下,我们需要简单地同意不同意。
【解决方案6】:

如果属性有一个私有集,那么它对外界是只读的,即:

string _name;
public string Name
{
     get{ return _name; }
     private set { _name = value; }
}

或者,如果它根本没有设置器,则可以将其设置为只读,即:

string _name;
public string Name
{
     get{ return _name; }
}

【讨论】:

    【解决方案7】:

    【讨论】:

      猜你喜欢
      • 2011-08-11
      • 1970-01-01
      • 2021-10-16
      • 2011-07-21
      • 2013-03-05
      • 2015-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多