C# 抽象 Dispose 方法

发布于 2024-08-10 09:18:32 字数 469 浏览 12 评论 0原文

我有一个实现 IDisposable 的抽象类,如下所示:

public abstract class ConnectionAccessor : IDisposable
{
    public abstract void Dispose();
}

在 Visual Studio 2008 Team System 中,我对我的项目运行代码分析,出现的警告之一如下:

Microsoft.Design :修改“ConnectionAccessor.Dispose()”,以便它调用 Dispose(true),然后对当前对象实例(Visual Basic 中的“this”或“Me”)调用 GC.SuppressFinalize,然后返回.

它只是愚蠢地告诉我修改抽象方法的主体,还是应该在 Dispose 的任何派生实例中做进一步的事情?

I have an abstract class that implements IDisposable, like so:

public abstract class ConnectionAccessor : IDisposable
{
    public abstract void Dispose();
}

In Visual Studio 2008 Team System, I ran Code Analysis on my project and one of the warnings that came up was the following:

Microsoft.Design : Modify 'ConnectionAccessor.Dispose()' so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns.

Is it just being silly, telling me to modify the body of an abstract method, or should I do something further in any derived instance of Dispose?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

彩虹直至黑白 2024-08-17 09:18:32

您应该遵循实现 Dispose 的传统模式。将 Dispose() 设为虚拟被认为是不好的做法,因为传统模式强调在“托管清理”中重用代码(API 客户端直接或通过 调用 Dispose()使用)和“非托管清理”(GC 调用终结器)。提醒一下,模式是这样的:

public class Base
{
    ~Base()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // so that Dispose(false) isn't called later
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
             // Dispose all owned managed objects
        }

        // Release unmanaged resources
    }
}

这里的关键是,用于非托管清理的终结器和 Dispose 之间没有重复,但任何派生类都可以扩展托管和非托管清理。

对于您的情况,您应该做的是:

protected abstract void Dispose(bool disposing)

并将其他一切保留原样。即使这也具有可疑的价值,因为您现在强制派生类实现 Dispose,并且您如何知道所有派生类都需要它?如果您的基类没有什么可处理的,但大多数派生类可能会这样做(也许有一些例外),那么只需提供一个空的实现即可。这就是 System.IO.Stream(本身是抽象的)所做的事情,因此有先例。

You should follow the conventional pattern for implementing Dispose. Making Dispose() virtual is considered bad practice, because the conventional pattern emphasizes reuse of code in "managed cleanup" (API client calling Dispose() directly or via using) and "unmanaged cleanup" (GC calling finalizer). To remind, the pattern is this:

public class Base
{
    ~Base()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // so that Dispose(false) isn't called later
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
             // Dispose all owned managed objects
        }

        // Release unmanaged resources
    }
}

Key here is that there's no duplication between finalizer and Dispose for unmanaged cleanup, and yet any derived class can extend both managed and unmanaged cleanup.

For your case, what you should do is this:

protected abstract void Dispose(bool disposing)

and leave everything else as is. Even that is of dubious value, since you're enforcing your derived classes to implement Dispose now - and how do you know that all of them need it? If your base class has nothing to dispose, but most derived classes likely do (with a few exceptions, perhaps), then just provide an empty implementation. It is what System.IO.Stream (itself abstract) does, so there is precedent.

国粹 2024-08-17 09:18:32

该警告基本上告诉您实施Dispose 模式 在你的班级里。

结果代码应如下所示:

public abstract class ConnectionAccessor : IDisposable
{
    ~ConnectionAccessor()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
    }
}

The warning basically tells you to implement the Dispose pattern in your class.

The resulting code should look like this:

public abstract class ConnectionAccessor : IDisposable
{
    ~ConnectionAccessor()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
    }
}
拥抱我好吗 2024-08-17 09:18:32

虽然这看起来确实有点挑剔,但这个建议是有效的。您已经表明您希望 ConnectionAccessor 的任何子类型都将拥有需要处置的某些内容。因此,确保基类完成正确的清理(就 GC.SuppressFinalize 调用而言)似乎比依赖每个子类型来完成似乎更好。

我使用 Bruce Wagners 书中提到的处置模式 Effective C# 基本上是:

public class BaseClass : IDisposable
{
    private bool _disposed = false;
    ~BaseClass()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        _disposed = true;
    }
}

public void Derived : BaseClass
{
    private bool _disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (_disposed) 
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        base.Dispose(disposing);
        _disposed = true;
    }

While it does seem a little like nit-picking, the advice is valid. You are already indicating that you expect any sub-types of ConnectionAccessor will have something that they need to dispose. Therefore, it does seem better to ensure that the proper cleanup is done (in terms of the GC.SuppressFinalize call) by the base class rather than relying on each subtype to do it.

I use the dispose pattern mentioned in Bruce Wagners book Effective C# which is basically:

public class BaseClass : IDisposable
{
    private bool _disposed = false;
    ~BaseClass()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        _disposed = true;
    }
}

public void Derived : BaseClass
{
    private bool _disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (_disposed) 
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        base.Dispose(disposing);
        _disposed = true;
    }
不念旧人 2024-08-17 09:18:32

我对迄今为止提供的答案的唯一抱怨是,他们都假设您需要有一个终结器,但事实并非如此。与终结相关的性能开销相当大,如果没有必要,我不想将其强加于我的所有派生类。

请参阅 Joe 的此博文 Duffy,它解释了您何时可能需要或不需要终结器,以及在任何情况下如何正确实现 Dispose 模式。
总结 Joe 的博客文章,除非您正在做一些相当低级的处理非托管内存的事情,否则您不应该实现终结器。作为一般经验法则,如果您的类仅保存对本身实现 IDisposable 的托管类型的引用,则不需要终结器(但应该实现 IDisposable 并处置这些资源)。如果您直接从代码分配非托管资源(PInvoke?)并且必须释放这些资源,那么您需要一个。如果派生类确实需要,则始终可以添加终结器,但是通过将其放入基类来强制所有派生类具有终结器会导致所有派生类受到可终结对象的性能影响,而这种开销可能不会增加。必要的。

The only gripe I'd have with the answers provided so far is that they all assume that you need to have a finalizer, which isn't necessarily the case. There is a fairly significant performance overhead associated with finalization, which I wouldn't want to impose on all of my derived classes if it wasn't necessary.

See this blog post by Joe Duffy, which explains when you may or may not need a finalizer, and how to properly implement the Dispose pattern in either case.
Summarizing Joe's blog post, unless you're doing something fairly low-level dealing with unmanaged memory, you shouldn't implement a finalizer. As a general rule of thumb, if your class only holds references to managed types that implement IDisposable themselves, you don't need the finalizer (but should implement IDisposable and dispose of those resources). If you are allocating unmanaged resources directly from your code (PInvoke?) and those resources must be freed, you need one. A derived class can always add a finalizer if it really needs it, but forcing all derived classes to have a finalizer by putting it in the base class causes all derived classes to be impacted by the performance hit of finalizable objects when that overhead may not be necessary.

給妳壹絲溫柔 2024-08-17 09:18:32

不过这个警告很有趣。 C# 设计师之一 Eric Lippert 在博客中阐述了为什么错误消息应该是“诊断性而非规范性:描述问题,而不是解决方案”。
在此处阅读.

The warning is interesting though. Eric Lippert, one of the C# designers, blogged about why error messages should be "Diagnostic but not prescriptive: describe the problem, not the solution".
Read here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文