为什么 C# 项目中的 /optimize 生成的代码分析警告比未启用此选项时多?

Why is /optimize in a C# project generating more Code Analysis warnings than without this enabled?

我们有一个项目,我们使用 StyleCop 和代码分析来验证代码的结构。我们已经为这两种机制设置了将警告视为错误。

然而,我们发现了一种我们无法解释的奇怪行为。我们有调试和发布配置。在我们的调试配置中,我们没有收到一个 CA 警告,而在我们的发布配置中收到此警告。我们开始研究这两种配置之间的差异,我们发现优化复选框是我们在发布期间而不是在调试期间收到此警告的原因。

我们有下面的配置。

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
</PropertyGroup>

当我们将调试的优化值设置为 true 时,我们也会收到 CA 警告。当我们将其设置为 false 时,警告消失了。这也仅适用于警告 CA1806。无论优化值如何,其他 CA 警告都会正确显示。

触发此警告的代码是下面的代码。这只是测试代码,但它模拟了我们遇到的真实情况。已分配默认值但从未在任何代码中使用的变量。

public CourseService(IOrtContext context)
{
    this.context = context;
    var defaultDate = new DateTime(1900, 1, 1);
}

那么,有人知道为什么显示 CA1806 取决于是否启用优化吗?

在这里随意扔石头...更简单的调试代码:http://goo.gl/8TXmE9 and release: http://goo.gl/XRBfQp .

在调试模式下,局部变量的生命周期是"extended"直到方法结束,这样更容易调试。在发布模式下,这不会发生。变量被积极地释放。如果您在调试模式或发布模式下编译程序并尝试调试它,您可以看到它......在发布模式下有时无法访问某些变量,因为它们已经结束了它们的生命周期。

这个可以在release IL代码中看到:

IL_0007: newobj instance void [mscorlib]System.DateTime::.ctor(int32, int32, int32)
IL_000c: pop

new DateTime() 的 return 立即被 pop 出栈。 CA1806可以轻松检测到。

在调试模式下,值永远不会从堆栈中弹出。检查它是否在某处使用的分析将非常复杂,可能还没有完成。

我相信那是因为优化器完全忽略了对 defaultDate 的赋值。

在调试模式下,实例化一个新的DateTime并分配给defaultDate局部变量:

var defaultDate = new DateTime(1900, 1, 1);

该赋值被视为 DateTime 实例的 "use",因此不会引发 CA1806,即使随后未使用 defaultDate

另一方面,优化器在释放模式下省略了赋值(和局部变量):

/* var defaultDate = */ new DateTime(1900, 1, 1);

因此 DateTime 实例不再被视为 "used",并引发 CA1806