在 Windows 上的 VS2010、VS2008 或 MonoDevelop 中编译以下程序时,我收到警告 CS0219,“变量 'y' 已赋值,但从未使用过它的值”。
namespace Problem
{
public class Program
{
private static void Main(string[] args)
{
object x = new object();
int y = 0;
}
}
}
为什么在 Visual Studio 中编译时没有出现
x
的警告?
有趣的是,在 Mac OS X 上的 MonoDevelop 中编译时,我确实收到了针对
x
和 y
的 CS0219 警告。
事实证明,当赋值操作的右侧不是编译时常量时,此警告会被抑制。
微软 Visual Studio 反馈网站上的一篇已被删除的帖子解释说,这是因为他们收到了很多来自那些纯粹分配变量的人的抱怨,这样他们就可以在调试过程中看到方法调用返回的内容,并发现警告令人恼火:
在这种情况下抑制“已分配但从未使用”警告 受到这样做的用户反馈的激励:
int Blah(){ // blah BlahBlah(x, y, z) // blah // blah }
“嘿,”用户在调试时说道,“我想知道 BlahBlah 是什么 返回?”但是没有简单的方法来检查返回值 调试器,因此用户经常这样做:
int Blah() { // blah int temp = BlahBlah(x, y, z) // blah // blah }
然后使用本地或监视窗口检查温度。温度是 从未在该函数的其他任何地方使用过,因此它产生了令人恼火的结果 “已分配但未读取”警告。
我认为这有点遗憾:
无论如何,我明白你无法取悦所有人。
我可能会离开这里,但我认为这是因为 y 仅被设置,而 x 被实例化为一些不平凡的东西 - 实例化可能涉及 New() 方法中的单独操作,并且因为实例化变量可能会产生副作用,它不被视为未使用。在你的情况下,它只是一个基础对象(),所以没有影响,但也许编译器不够聪明,无法区分。
另一方面,对于 y,实例化没有副作用,因此它被认为是未使用的 - 如果完全删除它,应用程序的代码路径将保持不变。
我的预感是,作为
x
一个引用类型,编译器不会显示任何警告,因为构造函数可能正在执行一些很可能“有意义”的操作;相反, y
是一种值类型,其值仅被分配但从未使用,编译器很容易告诉您,如果您不打算直接引用它,则这样做没有意义。
ReSharper 还会警告您 x 未使用。
y 在栈上,x 在堆上(线索:x 使用了 new 关键字)。因此,一些调试器/编译器警告可能会认为像 x 这样的堆/全局变量留给程序员担心,因为低级代码或调试探针可能会在幕后使用它们。但是像 y 这样的堆栈变量完全在编译器的控制之下,并且能够轻松地检测到未使用的冗余变量。
可能由于
x
是引用类型,因此存储在堆上,因此它会阻止该对象的垃圾回收,直到 x
超出范围。
例如:
void main(string[] args)
{
object x = new object();
while (true)
{
// Some threading stuff
// x is never garbage collected
}
}
对比:
void main(string[] args)
{
new object();
while (true)
{
// Some threading stuff
// The unreferenced object IS garbage collected
}
}
在 .NET 5 之前,您可以通过 FxCopAnalyzers NuGet 包(如 CA1804)捕获此错误,但在过渡到 roslyn-analyzers 期间不知何故中断了。
我发现正确捕获此错误的唯一方法(截至撰写本文时)是使用支持它的StyleChecker NuGet 包:
https://github.com/maroontress/StyleChecker/blob/main/doc/rules/UnusedVariable.md