【问题标题】:How do I dispose this code properly?如何正确处理此代码?
【发布时间】:2012-05-20 14:08:08
【问题描述】:

我有一个相当大的项目,我尽量保持干净整洁。当我在 Visual Studio 中运行代码分析器时,我遇到了一个我觉得很烦人的可靠性错误。我真的很想学习如何解决它。这是我正在做的一个简化示例。

这是警告。

警告 1 CA2000:Microsoft.Reliability:在方法“MyExampleClassForStackOverflow.AddFeed(string)”中,在对对象“new FeedClassExamle()”的所有引用超出范围之前调用 System.IDisposable.Dispose。

这是我的示例代码:

class MyExampleClassForStackOverflow : IDisposable
{
    public ConcurrentDictionary<string, FeedClassExamle> Feeds { get; set; }

    public void AddFeed(string id)
    {
        //The warning is coming from this code block.
        //In the full code, the feed classes collects data on a specific 
        //interval and feeds them back using events.
        //I have a bunch of them and they need to be accessible so I 
        //store them in dictionaries using keys to effeciently find them.
        Feeds.TryAdd(id, new FeedClassExamle());
        Feeds[id].Start();
    }
    public void Dispose()
    {
        foreach (var item in Feeds)
            item.Value.Dispose();
    }
}

class FeedClassExamle : IDisposable
{
    public void Start()
    {

    }
    public void Dispose()
    {

    }
}

为了测试代码,使用:

using (var example = new MyExampleClassForStackOverflow())
{

}

欢迎提出任何建议。

【问题讨论】:

  • 好问题。在这种情况下,我总是禁止显示此警告...如果有任何解决方法会很高兴

标签: c# dispose idisposable


【解决方案1】:

存在警告是因为代码分析工具无法确定对象是否会被正确处置。根据您的代码编写方式,对象实际上不会被正确处理,但修复代码可能不会消除警告。

从根本上说,需要为每个AddFeed 方法确保在它创建的每个FeedClassExample 实例上调用Dispose。最好的方法是避免创建 FeedClassExample 实例,如果一个实例已经存在于当前 ID 下的字典中。如果做不到这一点,AddFeed 方法应该处理它创建的任何FeedClassExample,然后决定不存储在字典中,或者与字典中的那个交换(我不确定ConcurrentDictionary支持这样做)然后Dispose旧的。基本要求是在AddFeed 的实际执行之外的任何时候,字典都将保存所有已创建但未销毁的FeedClassExample 实例。

在你的FeedClassExample 类中添加一个析构函数可能会提供有用的信息,除了记录一条消息之外什么都不做。如果您在该类上正确调用Dispose,则析构函数将永远不会执行。如果你没有打电话给Dispose,它会的。因此,如果析构函数曾经执行过,您就可以知道自己做错了什么。

【讨论】:

  • 是的,使用析构函数是个好主意。我可能会这样做。关于创建一个已经存在的新实例,这在我的主要代码中有所介绍。只是在我刚刚用来演示如何重现警告的简化示例中丢失了。
【解决方案2】:

如果 TryAdd 失败,对象不会被 Disposed,因此请尝试显式执行此操作:

public void AddFeed(string id)
{
    FeedClassExample fce = new FeedClassExamle();
    if (!Feeds.TryAdd(id, fce))
    {
        fce.Dispose();
    } 
    Feeds[id].Start();
}

【讨论】:

  • 我试过了,但仍然得到:警告 1 CA2000:Microsoft.Reliability:在方法“MyExampleClassForStackOverflow.AddFeed(string)”中,在对对象“fce”的所有引用之前调用 System.IDisposable.Dispose超出范围。
  • 如果你这样做Feeds[id] = new FeedClassExamle();,你会得到同样的信息吗?
  • 这似乎与 ConcurrentDictionary 类有关 - 当它被标准字典类替换时,该消息就会消失。看起来代码分析没有意识到 ConcurrentDictionary 正在保留引用。
  • @EugeneRyabtsev - 是的,同样的警告。
  • @BlueVoodoo:如果您确定自己在做什么,您也可以取消警告:LINK
【解决方案3】:

仅在需要添加时才创建实例:

if (!Feeds.ContainsKey(id)) {
  Feeds.GetOrAdd(id, new FeedClassExamle());
}

【讨论】:

  • 是的,这只是重现警告的示例代码。如果密钥存在,我的主要代码在添加之前返回。
  • @digEmAll: 对...使用GetOrAdd 方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-10
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
  • 2012-05-22
相关资源
最近更新 更多