Interfaces using IDisposable

2019-08-05 19:31发布

问题:

In regards to IDisposable

I'm creating interface that I would expect to use system resources most of the time, but not always. Would it be prudent to anticipate the usage include IDisposable on my Interface?

For example I have an interface that provides a mean to synchronize to.

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}
}

So the question is, should I impose the IDisposable requirement when I expect most implementations will be using system resources that need to be released? Currently I do just that as it is then easier when the user of the IDateTimeProvider is releasing resources and

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

would not be needed.

回答1:

So the question is, should I impose the IDisposable requirement when I expect most implementations will be using system resources that need to be released?

This is debatable, but there are examples in the framework that follow this guideline. A good example is Stream - it implements IDisposable even though there are subclasses where this is not necessary.

I would use caution about requiring this of your users, however, unless you truly are fairly certain that nearly all implementations will require IDisposable, and not just a few of them.



回答2:

Generally the reason for providing an interface is to allow programmers to treat various implementations of a concept as having the same set of behavior. If you implement IDisposable only on the classes that happen to manage system resources, you force programmers to deal with that implementation detail, adding complexity and fragility to your design.

If there's a chance that unmanaged resources might be referenced by your application when an object's usefulness expires, you should absolutely implement the IDisposable interface so that consumers of your class can use the Dispose Pattern to release those resources in a predictable manner.

As a reminder about the reason for the Dispose Pattern:

In computer programming, the dispose pattern is a design pattern which is used to handle resource cleanup in runtime environments that use automatic garbage collection. The fundamental problem that the dispose pattern aims to solve is that, because objects in a garbage-collected environment have finalizers rather than destructors, there is no guarantee that an object will be destroyed at any deterministic point in time. The dispose pattern works around this by giving an object a method (usually called Dispose or similar) which frees any resources the object is holding onto.

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



回答3:

The $50,000 question is whether code will ever acquire ownership of objects which implement the interface without knowing their specific type and whether they require cleanup. In situations where that could occur [by fast the most common examples being IEnumerable.GetEnumerator() and IEnumerable<T>.GetEnumerator(), though many other examples exist], client code must use one of two patterns:

  • If the interface type does not implement IDisposable [as is the case with the non-generic IEnumerator returned by non-generic IEnumerable], then properly-written client code that has acquired ownership of the object must--before abandoning it--check whether the particular object implementing the interface also happens to implement IDisposable and, if so, call its IDisposable.Dispose implementation.

  • If the interface does implement IDisposable, a properly-written client that has acquired ownership must--before abandoning the object--call IDisposable.Dispose on the object unless it somehow knows that the object doesn't actually need cleanup. Note that even if an object doesn't do anything in its IDisposable.Dispose method, it will in many cases be faster to call it unconditionally than to spend any significant time determining whether the call is necessary. Even if only 0.001% of the object instances would need cleanup, including IDisposable wouldn't impart any new obligations to the client code. Rather, it would increase the likelihood that client code would fulfill its obligations, and reduce the cost of doing so for all objects--including the 99.999% of instances don't require cleanup.

If an interface is unlikely to ever be returned by factory methods nor used in any other scenario where the owner of an object wouldn't know whether cleanup is required, there's no need for the interface to implement IDisposable. But for cases where the interface type may be returned by factory methods, it should implement IDisposable.