1

关于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();

不需要。

4

3 回答 3

1

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

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

作为对 Dispose Pattern 原因的提醒:

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

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

于 2013-03-29T16:16:31.047 回答
1

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

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

但是,我会谨慎地要求您的用户这样做,除非您确实相当确定几乎所有实现都需要IDisposable,而不仅仅是其中的几个。

于 2013-03-29T16:16:40.510 回答
1

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

  • 如果接口类型没有实现IDisposable[就像 non-genericIEnumerator返回的非泛型的情况一样IEnumerable],那么正确编写的已经获得对象所有权的客户端代码必须——在放弃它之前——检查特定对象是否实现接口也恰好实现IDisposable,如果是,调用它的IDisposable.Dispose实现。

  • 如果接口确实实现IDisposable了,那么获得所有权的正确编写的客户端必须——在放弃对象之前——调用IDisposable.Dispose该对象,除非它以某种方式知道该对象实际上并不需要清理。请注意,即使一个对象在其IDisposable.Dispose方法中没有做任何事情,在许多情况下,无条件调用它也比花费大量时间确定调用是否必要要快。即使只有 0.001% 的对象实例需要清理,IDisposable也不会给客户端代码带来任何新的义务。相反,它将增加客户端代码履行其义务的可能性,并降低所有对象这样做的成本——包括 99.999% 的实例不需要清理。

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

于 2013-04-01T17:12:09.143 回答