【问题标题】:C# Compiler should give warning but doesn't?C# 编译器应该给出警告但没有?
【发布时间】:2010-04-29 21:27:31
【问题描述】:

我的团队中有人尝试在空的 catch 子句中修复“变量未使用”警告。

try { ... } catch (Exception ex) { }

-> 给出关于ex 未被使用的警告。到目前为止,一切顺利。

修复是这样的:

try { ... } catch (Exception ex) { string s = ex.Message; }

看到这个,我想“太好了,所以现在编译器会抱怨 s 没有被使用。”

但事实并非如此!那段代码没有警告,我不知道为什么。有什么想法吗?

PS。我知道屏蔽异常的包罗万象的条款是一件坏事,但这是一个不同的话题。我也知道通过做这样的事情可以更好地消除最初的警告,这也不是重点。

try { ... } catch (Exception) { }

try { ... } catch { }

【问题讨论】:

  • 提示:请改用catch (Exception)。如果要在调试器中查看异常,请使用$exception

标签: c# compiler-construction warnings try-catch


【解决方案1】:

在这种情况下,编译器检测到 s 已写入但未读取,并故意抑制警告。

原因是因为 C# 是一种垃圾收集语言,信不信由你。

你是怎么理解的?

好吧,考虑以下几点。

你有一个程序调用了一个返回字符串的方法 DoIt()。您没有 DoIt() 的源代码,但您希望在调试器中检查它的返回值是什么。

现在在您的特定情况下,您使用 DoIt() 是因为它的副作用,而不是它的返回值。所以你说

DoIt(); // discard the return value

现在你正在调试你的程序,你去查看 DoIt() 的返回值,它不存在,因为当调试器在调用 DoIt() 后中断时,垃圾收集器可能已经已经清理了未使用的字符串

事实上,托管调试器没有“查看前一个方法调用返回的东西”的功能。非托管 C++ 调试器具有该功能,因为它可以查看被丢弃的返回值仍位于其中的 EAX 寄存器,但在托管代码中无法保证返回的值在被丢弃时仍然存在。

现在,有人可能会争辩说这是一个有用的功能,调试器团队应该添加一个功能,如果在方法执行后立即有一个调试器断点,则返回的值会保持活动状态。那将是一个不错的功能,但我不是要求它的人;去问问调试团队吧。

可怜的 C# 开发人员该怎么办?创建一个局部变量,将结果存储在局部变量中,然后在调试器中检查局部变量。调试器确实确保本地人不会被积极地收集垃圾。

所以你这样做了,然后编译器会给你一个警告,说你有一个只被写入并且从不读取的本地文件 因为读取的东西不是程序的一部分,它是开发人员坐在在那里看调试器。这是一种非常令人讨厌的用户体验!因此,我们检测到将 非常量 值分配给从不读取局部变量或字段的情况,并抑制该警告.如果您更改代码以改为显示string s = "hello";,那么您将开始收到警告,因为编译器原因,好吧,这不可能是有人在解决调试器的限制,因为值是 就在此处,开发人员无需调试器就可以读取它。

这就解释了这一点。在许多其他情况下,我们会禁止有关从未读取过的变量的警告; detailed exegisis of all the compiler's policies 表示我们何时报告警告以及何时不报告,这将花费我相当长的时间来写,所以我想我会留在那里。

【讨论】:

    【解决方案2】:

    变量 s 用于...保存对 ex.Message 的引用。如果你只有 string s;你会收到警告。

    【讨论】:

    • 我猜你是对的。 “对象 a = String.Empty;”也不显示编译器警告。它似乎只对值类型(不可为空)给出警告
    • 但是如果你只是分配一个变量而不使用它,编译器会说The variable 'x' is assigned but its value is never used
    • 这只发生在某些类型上,我特别相信值类型。如bool。但是引用类型,例如string,传统上不会抱怨。
    • Re: ccornet - True - 编译器似乎在赋值时将 ref 类型视为“已使用”。值类型在第一次访问时被视为“使用”。
    • 如果您想回复某人的评论,给@Gary 的提示,请使用@ 和该人的显示名称。如果您过去没有发现此功能,请参阅here 了解更多详情。
    【解决方案3】:

    我认为回答这个问题的人需要对编译器的工作原理有所了解。然而,像 FxCop 这样的东西可能会捕捉到这一点。

    【讨论】:

      【解决方案4】:

      属性只是方法,没有什么可以阻止任何人在 ex.Message 属性中添加一些执行某些操作的代码。因此,虽然您可能不会对 s 做任何事情,但调用 ex.Message 可能具有价值......

      【讨论】:

        【解决方案5】:

        当变量可能被使用或可能不被使用时,编译器的工作并不是计算每一个实例和极端情况。有些很容易发现,有些则更成问题。谨慎行事是明智之举(尤其是当警告可以设置为错误时 - 想象一下,如果软件没有编译只是因为编译器认为你没有使用你曾经使用过的东西)。微软编译器团队特别说:

        "...我们对客户的指导 有兴趣发现未使用的 他们代码中的元素是使用 外汇警察。它可以发现未使用的字段 还有更多有趣的数据 你的代码。”

        -Ed Maurer, Development Lead, Managed Compiler Platform

        【讨论】:

          【解决方案6】:

          Resharper 会发现

          【讨论】:

            【解决方案7】:

            静态分析在今天所能完成的工作上有些局限。 (尽管正如 Eric 指出的那样,不是因为在这种情况下它不知道。)

            .NET 4 中的新代码契约大大增强了静态检查,我相信有一天你会得到更多关于此类明显错误的帮助。

            如果您尝试过代码契约,您就会知道对代码进行详尽的静态分析并不容易 - 每次编译后它可能会崩溃几分钟。静态分析是否能够在编译时发现每一个这样的问题?可能不会:见http://en.wikipedia.org/wiki/Halting_problem

            【讨论】:

            • 这个特殊问题与停机问题没有任何关系。编译器检测到警告条件并故意禁止报告它。
            • 好的,谢谢。我认为这是对当前编译器中进行静态分析的程度的限制,而不是它正在执行然后丢弃它!停止状态链接只是为了说明静态分析的难度。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-17
            • 1970-01-01
            • 1970-01-01
            • 2020-10-31
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多