为什么将 Dispose 用作常规方法不好?

Why using Dispose as a normal method is bad?

正在想办法。有人告诉我

Dispose is not just a method - it's equivalent to a destructor in other languages.

好的。 Msdn 对此也大声疾呼

然后

class Test : IDisposable
{
    public string Property { get; set; } = "Test";
    public void Dispose() => Console.WriteLine("Disposed, very scary");
}

class Program
{
    static void Main(string[] args)
    {
        var test = new Test();
        test.Dispose();
        test.Property = "123";  // but it's disposed OMG! do not do this!
        test.Dispose();

        using (var another = new Test())
            for (int i = 0; i < 10; i++)
            {
                another.Dispose();
                GC.Collect(); // or what should I call to make it crash?
            }

        Console.ReadKey();
    }
}

而且没有问题。

我的看法Dispose

如有错误请指正

P.S:投反对票意味着 "question is bad/not useful/has problem"。如果您只是不同意我的想法 - post 评论或回答。对和我现在想法一样的人会有用(因为我错了?然后证明)

没错,Dispose只是IDisposable的另一个方法。它只是有一个额外的好处,就是在 using() 作用域结束时能够自动调用 - 它 not 等同于析构函数,无论谁告诉你这并不真正理解他们的意思正在说。

我总是在可能的情况下使用 using() 块,但如果你小心的话,你可以手动调用 Dispose,它会产生相同的效果。

Dispose 只是一个方法,您可以像调用任何其他方法一样调用它。它总是通过 IDisposable 接口公开(是的,显然你可以命名一个方法 Dispose 而无需实现 IDisposable不要那样做! ).

但是,手动调用它 有时 代码味道,可能 应该使用 using 的代码味道.这里的 "manually" 是指在另一个 Dispose.

的实现之外调用 Dispose

调用两次应该也是安全的 documented:

If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times. Instance methods other than Dispose can throw an ObjectDisposedException when resources are already disposed.

(我的重点)

你应该打电话给 Dispose 两次吗? 不!。这也是一种代码的代码味道,不再控制它已经完成的事情和剩下要做的事情并最终做事 "just to be sure"。 也不要那样做!

所以如果你写的代码正确,肯定可以手动调用Dispose

Visual Studio 2015 年(最终)提供了一个与指南相符的实施 IDisposable 的建议模式。

这是 VS 在选择实现 disposable 模式时为您创建的存根。 TODO 指导我们完成实施。

class Disposable : IDisposable
{
    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.

            disposedValue = true;
        }
    }

    // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
    // ~Disposable() {
    //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
    //   Dispose(false);
    // }

    // This code added to correctly implement the disposable pattern.
    public void Dispose()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(true);
        // TODO: uncomment the following line if the finalizer is overridden above.
        // GC.SuppressFinalize(this);
    }
    #endregion
}

请注意使用标志来指示 Dispose 是否已被调用。此标志可用于在 类 实现的方法调用和属性上抛出 ObjectDisposedException 实例。

Dispose 的目的一直是清理非托管资源。但是,using 关键字的语法糖(需要一个 IDisposable)可以让您创建非常整洁的 'context' 模式 类 来管理资源访问,即使没有非托管资源使用过。

像往常一样,清楚地记录用法和意图,你不会出错,但如果你不实现接口,请避免使用名为 'Dispose' 的方法。

.NET 中的 Dispose 与其他语言中的析构函数之间存在一个关键区别:如果一个指针指向另一种语言中的对象并且该对象被删除,那么将有一个 invalid 指针并且没有办法对它做任何事情,包括确定它是否仍然有效,而不调用未定义的行为。相比之下,在 .NET 中,如果一个对象的引用获得 Disposed,则该引用将继续是对相关对象的有效引用。该对象可能会拒绝做很多事情,因为它已被处置,但这种拒绝肯定是由对象本身产生的。不涉及未定义的行为。

另一方面,当在其他语言(如 C++)中调用析构函数时,对象可以确定不再存在指向它的有效指针,但 Dispose 并非如此。因此,代码应该避免池化 public 面向对象,而是让每个对新对象的请求 return 一个新对象(它可能是池化对象的包装器)。如果在包装器之外不存在对池对象的引用,并且 Dispose 在将对象放入池之前 Dispose 使该引用无效,则可以通过将池对象封装在一个新的对象中来满足未来的对象请求包装器将再次持有对它的唯一引用。