我的团队中的某个人尝试在空的catch子句中修复“未使用的变量”警告。
try { ... } catch (Exception ex) { }
- >发出关于ex
未被使用的警告。到目前为止,非常好。
修复是这样的:
try { ... } catch (Exception ex) { string s = ex.Message; }
看到这一点,我想“很棒,所以现在编译器会抱怨没有使用 s
。”
但事实并非如此!这条代码没有警告,我无法弄清楚原因。有什么想法吗?
PS。我知道将异常静音的全能条款是一件坏事,但这是一个不同的话题。我也知道通过做这样的事情可以更好地消除初始警告,这也不是重点。
try { ... } catch (Exception) { }
或
try { ... } catch { }
答案 0 :(得分:25)
在这种情况下,编译器检测到s已写入但未读取,故意抑制警告。
原因是因为C#是一种垃圾收集语言,信不信由你。
你怎么想的?
好吧,请考虑以下事项。
你有一个程序调用一个返回字符串的方法DoIt()。您没有DoIt()的源代码,但是您希望在调试器中检查它的返回值是什么。
现在,在您的特定情况下,您使用DoIt()作为其副作用,而不是其返回值。所以你说
DoIt(); // discard the return value
现在你正在调试你的程序,然后你去查看DoIt()的返回值和它不在那里,因为调试器在调用DoIt()后中断时,垃圾收集器可能有已经清理了未使用的字符串。
事实上,托管调试器没有“查看前一个方法调用返回的东西”的工具。非托管C ++调试器具有该功能,因为它可以查看丢弃的返回值仍然存在的EAX寄存器,但是您无法保证托管代码中的返回值在被丢弃时仍然有效。
现在,有人可能会认为这是一个有用的功能,并且调试器团队应该添加一个功能,如果在方法执行后立即出现调试器断点,则返回值保持活动状态。这将是一个很好的功能,但我是错误的人要求它;去询问调试组。
可怜的C#开发人员做什么?创建局部变量,将结果存储在局部变量中,然后在调试器中检查本地。调试器确实确保本地人不会积极地收集垃圾。
所以你这样做然后编译器会给你一个警告,你有一个只写入的本地,从不读,因为读取的东西不是程序的一部分,它是开发人员坐着在那里看着调试器。这是一个非常恼人的用户体验!因此,我们检测到非常量值被分配给局部变量或字段从未读取的情况,并禁止该警告。如果你更改了你的代码,而不是它string s = "hello";
,那么你将开始收到警告,因为编译器的原因,嗯,这可能不是一个解决调试器限制的人,因为值是< em>就在那里开发人员可以在没有调试器的情况下读取它。
这解释了一个。在许多其他情况下,我们会禁止有关从未读取的变量的警告;一个detailed exegisis of all the compiler's policies当我们报告警告时,当我们不这样做会花费我相当长的时间来写,所以我想我会留下它。
答案 1 :(得分:9)
使用变量s来保存对ex.Message的引用。如果你只是字符串s;你会得到警告。
答案 2 :(得分:1)
我认为回答这个问题的人需要深入了解编译器的工作原理。但是,像FxCop这样的东西可能会抓住这个。
答案 3 :(得分:1)
属性只是方法,没有什么可以阻止任何人在ex.Message属性中放置一些代码。因此,虽然你可能没有对s做任何事情,但调用ex.Message COULD可能有价值......
答案 4 :(得分:1)
当变量可能使用或不使用时,编译每个实例和极端情况并不是编译器的工作。有些很容易发现,有些则更成问题。谨慎的做法是明智的做法(特别是当警告可以被设置为错误时 - 想象一下软件是否因为编译器认为你没有使用你的东西而不能编译)。 Microsoft Compiler团队专门说:
“...我们对客户的指导 有兴趣发现未使用的 他们的代码中的元素是使用 的FxCop。它可以发现未使用的字段 还有更多有趣的数据 你的代码。“
答案 5 :(得分:1)
Resharper会抓住那个
答案 6 :(得分:0)
静态分析在今天可以完成的任务中受到限制。 (虽然埃里克指出不是因为在这种情况下它不知道。)
.NET 4中的新代码契约大大增强了静态检查,有一天我确信你会得到更多有关此类明显错误的帮助。
如果你已经尝试过代码契约,那么你会知道对代码进行详尽的静态分析并不容易 - 它可以在每次编译后抖动几分钟。静态分析是否能够在编译时找到这样的每个问题?可能不是:见http://en.wikipedia.org/wiki/Halting_problem。