【问题标题】:Why does this method cause Code Analysis error CA2000: Call Dispose()为什么这个方法会导致代码分析错误 CA2000: Call Dispose()
【发布时间】:2014-10-30 01:09:13
【问题描述】:

我正在使用“Microsoft Minimal Rules”代码分析集构建我的项目,它在此方法上为我提供了 CA2000:

private Timer InitializeTimer(double intervalInSeconds)
{
    Timer timer = null;

    try
    {
        timer = new Timer { Interval = intervalInSeconds * 1000, Enabled = true };
        timer.Elapsed += timer_Elapsed;
        timer.Start();
    }
    catch
    {
         if (timer != null)
         {
             timer.Dispose();
         }
    }
    return timer;
}

这个方法只是从一个以秒为单位的时间间隔创建一个新的System.Timers.Timer。我有三个这样的计时器正在运行(每秒一个,每分钟一个和每半小时一个)。也许最好有一个计时器并检查经过的事件处理程序是否已经过了一分钟或半小时,但我不知道,这在这个时候更容易,它是继承的代码,我不想破坏一切还没有。

这个方法让我臭名昭著

Warning 21  CA2000 : Microsoft.Reliability : In method 'TimerManager.InitializeTimer(double)', call System.IDisposable.Dispose on object '<>g__initLocal0' before all references to it are out of scope.

现在我正在调用 Dispose 并认为这就足够了?我还在类自己的 IDisposable 实现中处理所有计时器。

我在这里错过了什么?

【问题讨论】:

  • 取出对象初始化器语法 - 这就是触发警告的原因(因为对象存在,构造,但在初始化器运行时未分配给timer。我试图找到欺骗问题处理这个。
  • 好吧,不要为了愚蠢的工具而抛弃初始化语法。警告是虚假的,在调用 Start() 之前没有什么可处理的。吞下异常并返回一个已处理的对象,现在 将是值得抱怨的事情。
  • @HansPassant - 由于Interval 可以抛出ArgumentException,因此分析正确的是,此一次性对象将在没有调用Dispose 的情况下泄漏。没有任何记录(我可以找到)说如果 Start 没有被调用,你不需要 Dispose 计时器(尽管这可能适用于当前的实现)。
  • 如果记录在案,我们就不会拥有这个网站。有时我们不得不使用你自己的脑袋。

标签: c# .net timer code-analysis


【解决方案1】:

只有在发生异常时才调用Dispose(顺便说一句,您永远不应该使用 catch-all 块来处理,但那是另一回事了)。在没有异常的情况下,您不会释放 Timer 对象。

要么添加finally 块并将Dispose 移动到那里,要么使用using 块。

【讨论】:

  • 既然定时器是在“非异常”时返回的,这不应该没问题吗?或者更确切地说,为什么要总是处置它?
  • 好的,但是我希望计时器在该方法之后仍然存在,因为它被分配给我班级中的一个字段。我在 IDisposable 实现中处理我的字段。我的 try-catch 试图抑制警告。
  • @pst 是的,应该没问题,但 CodeAnalysis 可能无法检测到它。你可以压制那个特定的事件。
【解决方案2】:

警告告诉您,您正在创建一次性对象,而不是在所有情况下都将其丢弃。如果您以其他方法正确处理它,那么您可以安全地取消此警告(您可以使用SuppressMessageAttribute 来做到这一点)。

【讨论】:

  • 这就是我在其他情况下正在做的事情(Unity 的 LifeTimeManager),但在我在这里做之前想先检查一下。
  • 是的,对于长时间运行的对象(例如计时器),我经常收到该警告。它只是为了让您意识到您必须将对象放置在其他地方。
【解决方案3】:

好的,我是这样编辑的:

private Timer InitializeTimer(double intervalInSeconds)
    {
        Timer tempTimer = null;
        Timer timer;
        try
        {
            tempTimer = new Timer();
            tempTimer.Interval = intervalInSeconds * 1000;
            tempTimer.Enabled = true;
            tempTimer.Elapsed += timer_Elapsed;
            tempTimer.Start();
            timer = tempTimer;
            tempTimer = null;
        }
        finally
        {
            if (tempTimer != null)
            {
                tempTimer.Dispose();
            }
        }
        return timer;
    }

这是根据 CA2000 文档,它没有给出警告。我忽略了一个事实,即对象初始值设定项语法会创建一个可能不会被释放的临时对象。

谢谢各位!

【讨论】:

  • 您可能希望最终将其更改为重新抛出的捕获。否则,即使没有抛出异常,您也要在从方法返回之前处理掉您的计时器。
  • @NicoleCalinoiu 它仅在 temp 不为 null 且 temp 未到达 try 块的最后一行时才释放,这意味着抛出异常。这只是解决了必须捕获并重新处理异常的问题。
【解决方案4】:

我认为使用“使用”而不是“尝试/最终”要好得多reference

private Timer InitializeTimer(double intervalInSeconds)
{
    Timer timer;
    using (var tempTimer = new Timer())
    {
        tempTimer.Interval = intervalInSeconds * 1000;
        tempTimer.Enabled = true;
        tempTimer.Elapsed += timer_Elapsed;
        tempTimer.Start();
        timer = tempTimer;
    }
    return timer;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    • 1970-01-01
    • 2011-03-16
    • 2016-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多