【问题标题】:Interfaces using IDisposable使用 IDisposable 的接口
【发布时间】:2013-03-29 16:15:00
【问题描述】:

关于IDisposable

我正在创建我希望在大多数情况下使用系统资源的界面,但并非总是如此。预计在我的界面上使用包括IDisposable 是否谨慎?

例如,我有一个接口,它提供了一种同步方式。

interface IDateTimeProvider : IDisposable
{
    int LeapSeconds {get;set;}
    DateTime LocalNow {get;}
    DateTime UtcNow {get;}
    DateTime GpsNow {get;}
}

class NtpTimeProvider : IDateTimeProvider
{
    // Assume client is setup and ready to use.
    // Obtains time via network resources
    NtpClient client;  

   NtpTimeProvider (int leapSeconds)
   { LeapSeconds = leapSeconds;}

    int LeapSeconds {get;set;}
    DateTime LocalNow {get{return client.Utc};}
    DateTime UtcNow {get{return client.Utc};}
    DateTime GpsNow {get{return client.Utc - TimeSpan.FronSeconds(LeapSeconds);}}
    void Dispose()
    {
        if(client != null) Client.Dispose();
    }
}


class SystemTimeProvider : IDateTimeProvider
{

   SystemTimeProvider (int leapSeconds)
   { LeapSeconds = leapSeconds;}

    int LeapSeconds {get;set;}
    DateTime LocalNow {get{return DateTime.Now};}
    DateTime UtcNow {get{return DateTime.UtcNow };}
    DateTime GpsNow {get{return DateTime.UtcNow - TimeSpan.FronSeconds(LeapSeconds);}}
    void Dispose()
    { //obviously this isn't needed}
}

所以问题是,当我预计大多数实现将使用需要释放的系统资源时,我是否应该强加 IDisposable 要求?目前我就是这样做的,因为当 IDateTimeProvider 的用户正在释放资源并且

if(myDateTimeProvider is IDisposable) ((IDisposable)myDateTimeProvider).Dispose();

不需要。

【问题讨论】:

    标签: .net design-patterns idisposable


    【解决方案1】:

    所以问题是,当我预计大多数实现将使用需要释放的系统资源时,我是否应该强加 IDisposable 要求?

    这是值得商榷的,但框架中有一些示例遵循此指南。一个很好的例子是Stream - 它实现了IDisposable,即使有子类不需要这样做。

    但是,除非您确实相当确定几乎所有实现都需要IDisposable,而不仅仅是其中的一部分,否则我会谨慎对待要求您的用户。

    【讨论】:

    • 感谢 Reed,您总能得到很好的回应。这完美地回答了。目前我只能猜测一种需要使用系统资源的实现,所以我现在就要求它。
    【解决方案2】:

    通常,提供接口的原因是允许程序员将一个概念的各种实现视为具有相同的行为集。如果您仅在管理系统资源的类上实现IDisposable,您会迫使程序员处理该实现细节,从而为您的设计增加复杂性和脆弱性。

    如果当对象的有用性到期时,您的应用程序可能会引用非托管资源,则绝对应该实现IDisposable 接口,以便您的类的使用者可以使用 Dispose 模式以可预测的方式释放这些资源.

    提醒一下 Dispose Pattern 的原因:

    在计算机编程中,dispose 模式是一种设计模式,用于在使用自动垃圾收集的运行时环境中处理资源清理。 dispose 模式旨在解决的基本问题是,由于垃圾收集环境中的对象具有终结器而不是析构器,因此无法保证对象会在任何确定的时间点被销毁。 dispose 模式通过为对象提供一个方法(通常称为 Dispose 或类似方法)来解决此问题,该方法释放该对象所持有的任何资源。

    http://en.wikipedia.org/wiki/Dispose_pattern

    【讨论】:

    • 我认为这并不能真正解决问题。我认为问题更多的是“我的界面应该需要 IDisposable,还是只需要需要它的类?”
    • 确实,我确实尝试限制为仅需要的接口。幸运的是,为那些不需要它的对象实现 Dispose() 模式的复杂性非常简单,所以在这种情况下我会假设它不应该有问题。
    【解决方案3】:

    50,000 美元的问题是代码是否会在不知道具体类型以及是否需要清理的情况下获得实现接口的对象的所有权。在可能发生这种情况的情况下[最常见的例子是 IEnumerable.GetEnumerator()IEnumerable<T>.GetEnumerator(),尽管还有许多其他例子],客户端代码必须使用以下两种模式之一:

    • 如果接口类型没有实现IDisposable[如非泛型IEnumerator返回的非泛型IEnumerable的情况],则正确编写的客户端代码已获得所有权该对象必须在放弃它之前检查实现该接口的特定对象是否也恰好实现了IDisposable,如果是,则调用其IDisposable.Dispose 实现。

    • 1234563实际上需要清理。请注意,即使对象在其IDisposable.Dispose 方法中没有执行任何操作,在许多情况下,无条件调用它也比花费大量时间确定调用是否必要要快。即使只有 0.001% 的对象实例需要清理,包括 IDisposable 也不会赋予客户端代码任何新的义务。相反,它会增加客户端代码履行其义务的可能性,并降低所有对象这样做的成本——包括 99.999% 的实例不需要清理。

    如果一个接口不可能被工厂方法返回,也不太可能在对象所有者不知道是否需要清理的任何其他场景中使用,则该接口不需要实现IDisposable。但是对于接口类型可能被工厂方法返回的情况,应该实现IDisposable

    【讨论】:

    • 我想我同意。这就是我决定在接口上实施 IDisposable 的原因。我认为复杂性最终会减少潜在的问题,因为不需要它的实现者可以简单地实现一个空函数,但仍然没有什么重要的东西真的丢失了。感谢您的回复,这很有趣。
    • @galford13x:调用空的Dispose 函数非常便宜,以至于在大多数情况下,调用比检查是否有必要更快。只有一种情况我知道检查对象是否需要Dispose 的方法通常是一个重大胜利:在处理共享的不可变对象时,在不清楚最后一个用户是谁的情况下。在这种情况下,弄清楚何时调用Dispose 可能需要为任何可能最终成为最后一个消费者的消费者增加大量开销。不过,这种情况非常罕见。
    • 同意。感谢您的意见。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-15
    • 1970-01-01
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    • 1970-01-01
    相关资源
    最近更新 更多