【问题标题】:Why does Code Analysis fire CA2000 on this method?为什么代码分析会在此方法上触发 CA2000?
【发布时间】:2016-04-27 05:00:06
【问题描述】:

Visual Studio 代码分析在此方法中的 monitor 变量上生成警告“在失去范围之前处理对象”(CA2000)。

private void MonitorJob(IJob job, CancellationToken cancellationToken)
{
    var monitor = new JobMonitor(job, _backend);  // <- CA2000
    try
    {
        var task = monitor.Run(cancellationToken);
        _activeJobs[task] = monitor;
    }
    catch
    {
        monitor.Dispose();
        throw;
    }
}    

我了解 CA2000 的作用,而且我通常能够找出我的代码违反规则的原因并进行适当的更改。

然而,在这种情况下,我很难过 - 这真的是误报,还是我遗漏了什么?

使用 Visual Studio 2015 企业版,面向 .NET 4.5,使用 C# 6。

【问题讨论】:

  • 对于一个工具来说,Run() 方法总是 抛出异常并不明显。或者它可能会在以后被处理,因为你将它存储在 _activeJobs 中,假设你这样做了。只需取消警告即可。
  • 不使用using的原因是什么?
  • @qxg 你在这里怎么用?
  • @zerkms using(var monitor = new JobMonitor()) { var task ... }。监视器将被释放并抛出异常。
  • @qxg 你有没有注意到这条_activeJobs[task] = monitor; 行?

标签: c# visual-studio code-analysis


【解决方案1】:

如果此处抛出异常,您可能会泄漏此一次性用品:

private void MonitorJob(IJob job, CancellationToken cancellationToken)
{
    var monitor = new JobMonitor(job, _backend);

    // <- Exception

    try
    {
        var task = monitor.Run(cancellationToken);
        _activeJobs[task] = monitor;
    }
    catch
    {
        monitor.Dispose();
        throw;
    }
}

这可能是由 ThreadAbortException 或运行时注入线程的任何其他异常引起的。我建议在 try 块之外声明变量,但在其中分配它。另外,将其设置为null,以成功将其分配给_activeJobs

private void MonitorJob(IJob job, CancellationToken cancellationToken)
{
    JobMonitor monitor;

    try
    {
        monitor = new JobMonitor(job, _backend);
        var task = monitor.Run(cancellationToken);
        _activeJobs[task] = monitor;
        monitor = null;
    }
    finally
    {
        if(monitor!=null)
        {
            monitor.Dispose();
        }
        throw;
    }
}

即便如此,关闭警告可能还不够,此时我建议为其添加抑制功能。

【讨论】:

  • 这一切都说得通,但给我留下了一个(次要的)后续问题:为什么同一项目中的 other 代码也遵循var x = new Y(); try..catch 模式违反CA2000?
  • 例如,此处接受的答案stackoverflow.com/a/9663477/30280 在建议的“安全”版本CreateFirstObject() 中具有这种结构。
【解决方案2】:

我假设_activeJobs[task] = monitor; 是一个简单的赋值并且不会抛出异常。如果是这样,将存储监视器与创建监视器分开。

private void MonitorJob(IJob job, CancellationToken cancellationToken)
{
    Task task;
    var monitor = CreateJobMonitor(job, _backend, out task);
    _activeJobs[task] = monitor;
} 

private JobMonitor CreateJobMonitor(IJob job, CancellationToken cancellationToken, out Task task)
{
    var monitor = new JobMonitor(job, _backend);
    try
    {
        task = monitor.Run(cancellationToken);
        return monitor;
    }
    catch
    {
        monitor.Dispose();
        throw;
    }

这样,CreateJobMonitor 意味着要么返回一个有效对象,要么抛出异常。没有机会返回已处置的对象引用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 2018-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-07
    相关资源
    最近更新 更多