【问题标题】:Why doesn't the C# compiler stop properties from referring to themselves?为什么 C# 编译器不阻止属性引用自己?
【发布时间】:2010-02-25 19:14:36
【问题描述】:

如果我这样做,我会得到一个System.StackOverflowException

private string abc = "";
public string Abc
{
    get
    { 
        return Abc; // Note the mistaken capitalization
    }
}

我明白为什么 - 属性引用自身,导致无限循环。 (参见之前的问题herehere)。

我想知道(以及我在之前的问题中没有看到答案)是为什么 C# 编译器没有发现这个错误?它会检查一些其他类型的循环引用(类从自身继承等),对吗?只是这个错误不够普遍,不值得检查吗?或者是否有一些我没有想到的情况,当您希望属性以这种方式实际引用自身时?

【问题讨论】:

  • 通过“引用自身”,我想我的意思是“递归地调用自身”,但你明白了。

标签: c# compiler-construction exception


【解决方案1】:

你可以在最后一条评论here看到“官方”的原因。

Microsoft 于 2008 年 11 月 14 日发布于 19:52

感谢您的建议 视觉工作室!

你说得对,我们可以很容易地 检测属性递归,但我们 不能保证什么都没有 有用的正在完成 递归。财产的主体 可以在您的对象上设置其他字段 这会改变下一个的行为 递归,可以改变它的行为 基于来自控制台的用户输入, 甚至可以基于不同的行为 关于随机值。在这些情况下,一个 自递归属性确实可以 终止递归,但我们有 无法确定是否是这种情况 在编译时(不解决 停止问题!)。

由于上述原因(以及 需要做出重大改变 不允许这样做),我们将无法 禁止自递归属性。

亚历克斯·特纳

项目经理

Visual C# 编译器

【讨论】:

  • 如果他们产生一个关于它的编译器警告可能会很好。我认为这很有价值。
  • 有道理。 . .可以肯定地说,故意制作递归属性 getter 可能永远不是一个好主意,因为它只会在有副作用时停止,并且(如果我理解正确的话)方法是首选在有副作用的情况下到属性?
  • @Tim:这些副作用可能是内部副作用,通常是用户不可见的副作用。属性 getter 并非完全不可能产生副作用(例如,如果您想计算 get 被调用的次数)。好吧,还是很傻:)
  • @Nick 很多商店都非常严格,不允许编译带有警告的任何东西,所以他们对于可能不需要它们的东西产生警告非常严格。见blogs.msdn.com/ericlippert/archive/2010/01/25/…
  • @Tanzelax - 是的,但我发现您引用的链接中的示例很奇怪,因为他们明确提到“无用代码”不值得警告,但他们会产生警告“字段 foo永远不会使用”,如果您创建一个从未分配给的成员变量 foo。
【解决方案2】:

除了 Alex 的解释之外,还有一点是,我们会尝试对执行您可能不打算执行的操作的代码发出警告,这样您可能会不小心携带该错误

在这种特殊情况下,警告实际上可以为您节省多少时间?单次试运行。你会在测试代码的那一刻发现这个错误,因为它总是会立即崩溃并可怕地死掉。警告实际上不会给您带来太多好处。递归属性评估中存在一些微妙错误的可能性很低。

相比之下,如果您执行以下操作,我们发出警告:

int customerId; 
...
this.customerId= this.customerId;

没有可怕的死机,代码是有效代码;它为字段分配一个值。但由于这是无意义的代码,你可能不是故意的。由于它不会死得很惨,因此我们会发出警告,提示您可能不打算在此处发生某些事情,并且可能不会通过崩溃发现。

【讨论】:

  • 我在 Alex 的回答中讨论了这个 cmets。您的声明“但是由于这是无意义的代码,您可能不是故意的。因为它不会死得很惨,我们警告说这里有些东西您可能不打算这样做”似乎与您的博客相矛盾在这里发帖:blogs.msdn.com/ericlippert/archive/2010/01/25/…
  • @Nick:我没有看到矛盾。 (这并不意味着没有:我很大,我包含许多人。)我只是没有看到你在说什么。
  • @Eric Lippert:感谢您参考我自己的歌。 :-)
  • 谢谢你的两个回答,埃里克(这个和我今天早上关于如何发音你的名字的问题。)对于它的价值,你的立场对我来说似乎是一致的。您写道:“我们尝试仅针对那些我们几乎可以肯定地说代码已损坏、误导或无用的情况保留警告”。 this.customerId = this.customerId 毫无用处。
  • @Nick:两种情况的区别在于,在“未使用的使用”示例中,未使用的使用可能只是绒毛。它没有任何意义,它从来没有打算有意义,它可以在不改变语义的情况下被删除或保留。 “将变量分配给自己”警告几乎肯定是打算说别的。我们不仅指出代码是无用的,我们还指出代码可能是有目的的,但实际上是无用的,它们的组合很可能是错误。
【解决方案3】:

引用自身的属性并不总是导致无限递归和堆栈溢出。例如,这很好用:

int count = 0;
public string Abc
{
    count++;
    if (count < 1) return Abc;
    return "Foo";
}

上面是一个虚拟的例子,但我敢肯定有人会想出类似的有用的递归代码。编译器无法确定是否会发生无限递归(停止问题)。

在简单的情况下生成警告会很有帮助。

【讨论】:

    【解决方案4】:

    他们可能认为如果没有任何实际收益,编译器会不必要地复杂化。

    第一次调用此属性时,您会很容易发现此错字。

    【讨论】:

      【解决方案5】:

      首先,您会收到未使用变量abc 的警告。

      其次,递归没有什么不好,只要它不是无限递归。例如,代码可能会调整一些内部变量,然后递归调用相同的 getter。然而,编译器根本没有简单的方法来证明某些递归是无限的(任务至少是 NP)。编译器可以捕捉到一些简单的情况,但消费者会惊讶于更复杂的情况通过编译器的检查。

      【讨论】:

        【解决方案6】:

        它检查的其他情况(递归构造函数除外)是无效的 IL。
        此外,所有这些情况,甚至是递归构造函数)都保证会失败。

        但是,有可能(尽管不太可能)故意创建有用的递归属性(使用 if 语句)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-07-16
          • 2022-12-06
          • 1970-01-01
          • 2015-04-12
          • 2016-07-12
          • 2020-04-07
          • 2023-03-16
          相关资源
          最近更新 更多