【问题标题】:What is the expectation of IDisposable?IDisposable 的期望是什么?
【发布时间】:2013-10-24 01:33:08
【问题描述】:

我看到很多这样的代码:

public class MyWcfService : IMySerciceContract, IDisposable
{
    private DatabaseOperations _dataAccess;

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

    protected virtual void Dispose(bool disposing)
    {
        _dataAccess.Dispose();
    }
}

MyWcfService 类将托管在 IIS 或 WAS 中,没有人会在其中显式调用 Dispose。我的想法是让这个类实现 IDisposable 是没有意义的,你最好将 _dataAccess 的使用包装在 using 语句中。据我了解,实现 IDisposable 的类的期望是该类的用户将在 using 块声明中实例化它。考虑到没有用户会显式调用 Dispose,上述示例是否是不好的做法?如果我们像上面的例子那样依赖 GC 来清理,那么 GC 到底是调用 Dispose,还是只是调用 finalizer?

【问题讨论】:

  • 因为您有一个实现 IDisposable 的成员,所以理想情况下应该实现它...因为您的班级已成为所有者。请参阅 stackoverflow.com/questions/9472304/… ... 和 stackoverflow.com/questions/10956140/… ... 并运行 FXCop 以检测任何 IDisposable 违规。显然,任何使用您的类型的东西都需要调用 Dispose(显式或通过 using 隐式调用)。
  • GC 对 Dispose 或 IDisposable 一无所知。它只是调用终结器。
  • 但是在这样的服务类的情况下,我们甚至不期望调用 Dispose 的东西,这有什么意义呢?如果我知道没有人会真正调用 Dispose,我为什么要实现 IDisposable?我自己在这个服务类中清理 _dataAccess 不是更好吗?
  • 这就是为什么“推荐”的做法是在终结器中调用Dispose(false)。虽然对象 应该 被直接释放(通常由创建所述实例的人;在这种情况下,它应该是 WCF 主机,因此可以愉快地忽略终结器位),稍后运行通常比 running-never 更好。
  • "DatabaseOperations _dataAccess" 是一个类成员,由你的类“拥有”。它是 IDisposable,所以你的班级也应该是。如果 _dataAccess 是类方法中的局部变量,您在方法范围内使用并丢弃了它,那么 using 块将是合适的。

标签: c# .net wcf using


【解决方案1】:

“DatabaseOperations _dataAccess”是一个类成员,归你的类“所有”。它是 IDisposable,所以你的班级也应该是。并且您应该始终实现完整的标准处理模式以保持一致性。

如果 _dataAccess 是类方法内的局部变量,您在方法范围内使用和丢弃它,那么 using 块将是合适的。

通常,您希望以这样一种方式进行编码,以便为您的代码的用户提供最大程度的清理控制。这意味着如果您的任何成员都是 IDisposable。但是对于非成员 IDisposable 对象,“使用”是要走的路。

当然,这同样适用于您班级的消费者。如果他们将您的类嵌入为成员字段,他们应该使他们的类 IDisposable。否则,他们可以对您的类对象使用“使用”……等等。

【讨论】:

    【解决方案2】:

    ... ,没有人会显式调用 Dispose 。我的想法是让这个类实现 IDisposable 毫无意义......

    关键是,您的代码中缺少调用Dispose 的终结器..

    ...据我了解,实现 IDisposable 的类的期望是该类的用户将在 using 块声明中实例化它...

    通常,实现IDisposable 的类的期望是有东西要处理。 using 只是一个语法糖,它根本不是实现IDisposable 的原因。

    ...如果我们像上面的例子一样依靠 GC 来清理,GC 甚至会调用 Dispose,还是只是调用终结器?

    GC 不会自动调用Dispose,因此您需要让终结器调用它。

    public partial class MyWcfService: IMySerciceContract, IDisposable {
        private DatabaseOperations _dataAccess;
    
        public void Dispose() {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing) {
            if(!this.disposed) {
                if(disposing) {
                    _dataAccess.Dispose();
                }
    
                this.disposed=true;
            }
        }
    
        // so it make sense now .. 
        ~MyWcfService() {
            this.Dispose(false);
        }
    
        bool disposed;
    }
    

    如果消费者没有调用Dispose,则终结器会在必要时调用它。看看如何实现 [IDisposable interface]。

    【讨论】:

    • 不错的答案,尽管我完全知道我们不会为了使用语句而实现 IDisposable 大声笑。我很好地掌握了 RAII 的概念,我的问题更多是关于何时清理一次性物品,以及应该在哪里承担责任。
    • @Didaxis:对于何时,我认为当您确定以后不应该使用一次性物品时,我会尽快。如果_dataAccess被MyWcfService的实例获取,那么MyWcfService应该负责_dataAccess。对于 where,是我提出的代码,与 MSDN 指导的代码相同。
    • 一个类实现了IDisposable这一事实并不意味着有一些东西要处理。相反,期望是在调用对象的Dispose 方法之后,宇宙中的任何地方都不会有任何实体代表它做任何损害任何其他实体的事情。一个从不要求任何外部实体代表它做任何事情的对象将符合该期望,即使它的暴露方法什么都不做,因为在它运行之后(仍然)不会有外部实体代表它行事。
    • @KenKin:如果一个工厂方法有时会创建拥有资源的对象,并将所有权转移给它的调用者(主要示例:IEnumerable<T>.GetEnumerator(),它应该实现IDisposable。事实上它实现了@ 987654337@ 不会为工厂方法的调用者创建新的职责——这些职责是由于某些创建的对象可能拥有资源这一事实而创建的——而只是使调用者更容易执行这些职责并增加他们执行这些职责的可能性。会这样做的。
    • @KenKin:早点应用会更好;有一个工厂方法返回的东西不实现IDisposable本质上说,任何需要清理的未来产品工厂都应该被隔离到一个单独的“宇宙”中。有时这种隔离可能会让每个相关人员的生活更轻松,但这应该是一个深思熟虑的决定——而不是一个人不得不做的事情。
    猜你喜欢
    • 2012-04-01
    • 1970-01-01
    • 2017-11-26
    • 2015-04-21
    • 2020-11-09
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多