C#编译器应该发出警告但不是吗?

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

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

– >发出关于ex未被使用的警告。 到现在为止还挺好。

修复是这样的:

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

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

但事实并非如此! 这段代码没有警告,我无法弄清楚原因。 有任何想法吗?

PS。 我知道将exception静音的全能条款是一件坏事,但这是一个不同的话题。 我也知道通过做这样的事情可以更好地消除初始警告,这也不是重点。

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

要么

 try { ... } catch { } 

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

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

你觉得怎么样?

好吧,请考虑以下几点。

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

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

 DoIt(); // discard the return value 

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

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

现在,有人可能会认为这是一个有用的function,并且调试器团队应该添加一个function,如果在方法执行后立即出现调试器断点,则返回值保持活动状态。 这将是一个很好的function,但我是错误的人要求它; 去询问调试组。

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

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

这解释了一个。 在许多其他情况下,我们会禁止有关从未读取的变量的警告; 我们报告警告时所有编译器策略的详细说明,以及我们何时不会花费相当长的时间来编写,所以我想我会留下它。

变量s用于…保存对ex.Message的引用。 如果你只有字符串s; 你会得到警告。

我认为回答这个问题的人需要深入了解编译器的工作原理。 但是,像FxCop这样的东西可能会抓住这个。

属性只是方法,没有什么可以阻止任何人在ex.Message属性中放置一些代码。 因此,虽然您可能没有对s做任何事情,但调用ex.Message COULD可能具有价值….

当变量可能使用或不使用时,编译每个实例和极端情况并不是编译器的工作。 有些很容易发现,有些则更成问题。 谨慎谨慎是明智的做法(特别是当警告可以被设置为错误时 – 想象一下软件是否因为编译器认为你没有使用某些东西而无法编译)。 Microsoft Compiler团队专门说:

“…我们对有兴趣在代码中发现未使用元素的客户提供的指导是使用FxCop。它可以发现未使用的字段以及有关代码的更多有趣数据。”

– Ed Maurer,托管编译器平台开发主管

Resharper会抓住这个

静态分析在今天可以完成的任务中受到限制。 (虽然埃里克指出不是因为在这种情况下它不知道。)

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

如果你已经尝试过代码契约,那么你会知道对代码进行详尽的静态分析并不容易 – 它可以在每次编译后抖动几分钟。 静态分析是否能够在编译时找到这样的每个问题? 可能不是:请参阅http://en.wikipedia.org/wiki/Halting_problem 。