理解可丢弃对象

4

我在SO上查找了与此类似的问题,尽管我找到了相当多的答案,但是它们都没有为我解决这个问题提供任何帮助。

假设我有以下代码:

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

    protected virtual void Dispose(bool disposing) { }
}
  • 我需要在SuperObject上使用protected virtual void Dispose(bool)吗?因为实际上没有什么需要处理的。
public interface ICustom : IDisposable { }

public class Custom : ICustom
{
    public SuperObject Super { get; protected set; }

    public Custom()
    {
        Super = new SuperObject();
    }

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

    public virtual void Dispose(bool disposing)
    {
        if (!disposing) return;

        if (Super != null)
            Super.Dispose();
    }
}

public class Foo
{
    public Foo()
    {
        using (var c = new Custom())
        {
            //do magic with c
        }
    }
}

如果我想/需要/尝试在像 System.Web.Mvc.Controller 这样已经实现并且一直实现了 IDisposable 的类上使用 Custom,会发生什么?

public class Moo : Controller
{
    Custom c;

    public Moo()
    {
        c = new Custom();
    }

    // Use c throughout this class        
}

如何正确处理 Moo 中的 c


3
当没有需要Dispose的时候,不要实现IDisposable。 - H H
为什么在没有析构函数时要使用GC.SuppressFinalize() - Hamlet Hakobyan
@HamletHakobyan 这是一个好问题,也许你可以在这里提供帮助:https://dev59.com/KJnks4cB2Jgan1znqVdi。 - Esteban
2个回答

9
正常的做法是应用标准IDisposable实现 - 但是只有在您的类或某个派生自它的类将使用非托管资源时,这才是真正必要的 - 这种情况实际上非常罕见(而且当这种情况适用时,最好将非托管资源包装在其自己的类中,该类具有完整的标准IDisposable实现)。
因此,假设您没有处理非托管资源(原始文件句柄、全局分配的内存等),并且仅处理可处理的成员(即具有托管资源并实现IDisposable的成员),则可以安全地使用IDispose的最小实现 - 即:
只需有一个void Dispose()方法。 在该方法中,只需对可处理的成员调用dispose,然后在基类上调用Dispose(如果它是可处理的)。 如果您有一个类层次结构,则可以将此处Dispose设置为虚拟的。 没有必要有Dispose(bool)方法。 也没有必要检查对象是否已处理 - 因为您所做的就是在其他对象上调用dipsose,而这些实现将进行该检查。
如果您不喜欢最小化的方法,请应用标准完整实现(但这并非严格必要)。也就是说,要么按照推荐方法做标准实现,因为您坚持遵循推荐方法,要么进行简单的最小化(但正确)实现 - 但不要在两者之间做某些事情(即不标准,不简单或不正确)!
有关更多详细信息,请参见此问题:仅针对托管资源的最小IDispose实现 所以在您的情况下,以下是最小的实现:
public class SuperObject : IDisposable {
    public void Dispose() {
        // Dispose code...just call dispose on dispoable members.
        // If there are none then no need to implement IDisposable!
    }
}

public interface ICustom : IDisposable { }
public class Custom : ICustom {
    public SuperObject Super { get; protected set; }

    public Custom() {
        Super = new SuperObject();
    }

    public void Dispose() {
        if (Super != null)
            Super.Dispose();
    }
}  

public class Moo : Controller {
    Custom c;

    public Moo() {
        c = new Custom();
    }

    public Dispose() {
        if (c!=null)
            c.Dispose()
        base.Dispose();       
    }
}

请注意,如果Super对象没有任何可处置的资源,则实现IDisposable并具有Dispose方法是没有意义的。如果Customs只有可处置的对象是SuperObject,那么同样适用,同样的逻辑也适用于Moo。最后,如果所有上述情况都适用,并且周围没有其他可处置的对象,则您真正需要的只是:
  public class Moo : Controller {
        Custom c;
    
        public Moo() {
            c = new Custom();
        }
    
        public Dispose() {
           base.Dispose();       
        }
    }

“标准IDisposable实现”的更新文档链接在此:https://msdn.microsoft.com/zh-cn/library/fs2xkftw(v=vs.110).aspx” - Tiago Crizanto

3
如何在Moo中正确处理c?
public class Moo : Controller
{
    Custom c;

    public Moo()
    {
        c = new Custom();
    }

    // Use c throughout this class    


    protected override Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (disposing)
           c.Dispose()
    }
}

这也回答了您的第一个问题,Controller需要使其Dispose(bool)方法为protected virtual,否则上述操作将不可能实现。

但是需要注意以下几点:

  • 您没有任何isDisposed逻辑。最好只进行一次处理,并且可能要捕获在处理后使用的情况。
  • 省略析构函数(finalizer)本身是一个好主意,但现在您有额外的约束条件,即没有派生类应该拥有任何未托管的资源。

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