False-positive:修复此IDisposable实现以符合dispose模式。

21

我的类实现了IDisposable,并遵循模式

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

但是Sonar仍然告诉我需要实现dispose模式...

Sonar规则3881(IDisposable应该正确实现)

这是Sonar的缺陷还是我错过了什么?


也许它检测到您的类中没有实现终结器,因此 SuppressFinalize 调用无效? - Marek Fekete
此外,您的“Dispose(bool)”应该是受保护的吗?这至少是SonarQube明确指出的: protected virtual void Dispose(bool disposing) - Marek Fekete
  1. 如果是这样的话,它确实应该这么说,但我使用的模式与我实现的模式完全相同。
  2. 眼力不错,我会尝试一下,但不确定它是否有所关注。
- Shaun Wilde
它确实是这样的 :) TrackedMethodStrategyManager.Dispose() 应该只包含对 TrackedMethodStrategyManager.Dispose(true) 的调用,如果类包含终结器,则应调用 GC.SuppressFinalize(this) - Marek Fekete
我已经发布了答案,但我建议您接受@Valeri - SonarSource Team的答案,它更加完整。虽然很高兴能够帮助到您。 - Marek Fekete
显示剩余2条评论
3个回答

46

我看到您已经解决了问题,但是以防其他人遇到同样的问题,我将详细说明规则要求。

这个规则的想法是允许可能的派生类正确地处理您的类的成员。因此,如果您的类是密封的,该规则假定您的类正确地处置自己并且不会执行任何操作(还有另一个规则,S2931,检查您的类是否包含需要处置的IDisposable字段)。

如果该类未被密封,该规则将检查它是否具有实现IDisposable的基类。如果它有,并且您的类也实现了IDisposable,该规则将建议删除您的实现(例如,从您的类中删除IDisposable接口),并覆盖基类的protected Dispose(bool)方法。

如果基类未实现IDisposable,则该规则要求拥有一个protected virtual Dispose(bool)方法(以允许继承者正确处理您的类)。

如果您的类包含终结器(即析构函数),则该规则将检查其内容是否为单个调用Dispose(false)

该规则检查Dispose()方法(接口中的方法)的内容是否包含单个调用Dispose(true)。如果您的类具有终结器,则该规则需要另外调用GC.SuppressFinalize(this)

基本上,这些是根据规则正确实现IDisposable的方式:

密封类

public sealed class Foo1 : IDisposable
{
    public void Dispose()
    {
        // Cleanup
    }
}

简单实现

public class Foo2 : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        // Cleanup
    }
}

使用终结器实现

public class Foo3 : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        // Cleanup
    }

    ~Foo3()
    {
        Dispose(false);
    }
}

你打算将SonarQube中的合规样本更改以匹配上述内容吗? - Shaun Wilde
2
是的,我们会更改描述。 - Val
2
对我来说,即使我实现了“简单实现”,例如Foo2,我仍然收到警告。我改用此处显示的“密封类”实现,以消除警告。 - Matt Frear
7
如果类不是密封的,无论该类本身是否具有终结器,都必须始终使用GC.SuppressFinalize()。如果派生类中的一个决定实现终结器,则没有合适的方法将其添加到派生类中。请参见https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern#basic-dispose-pattern。 - bitbonk

2

除非你的类实现了终结器,否则不要在你的Dispose()方法中调用GC.SuppressFinalize()


如果类没有被密封,那么GC.SuppressFinalize() 必须始终存在,详见我上面的评论。 - bitbonk
@Marek:你能详细解释一下你的原因吗? - Thariama
1
@Thariama: 我的思路是从Sonar的信息中推断出可能的解决方案,尽管他的措辞不够严谨,但这表明调用可能是Sonar不想要的。至于是否真的应该遵守规则,我更倾向于支持@bitbonk的意见- 即使您没有实现终结器,请调用SuppressFinalize-继承者将是一个原因,如果稍后添加终结器,则可能会忘记添加它是另一个原因。 - Marek Fekete

1
在我的情况下,我缺少了virtual关键字:
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
    if (!disposed && disposing)
    {
        _dbContext.Dispose();
    }
    disposed = true;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接