C# IDisposable 问题
我有以下代码示例:
public interface IRepository {
// Whatever
}
public class SampleRepository : IRepository {
// Implements 'Whatever'
}
public class NHibernateRepository : IRepository, IDisposable {
// ...
public void Dispose() { ... }
}
现在 - 这真的很糟糕吗?我不确定,但这似乎与未在 < 中标记基类 virtual 的析构函数非常相似代码>C++。
我不想让 IRepository 接口实现 IDisposable,因为这会带来不必要的复杂性和一堆也必须实现 IDisposable 的类>。
应该如何处理这种情况?
我确信这种情况可能发生在任何类型层次结构上 - 当派生类型之一必须管理一次性资源时资源。
那么我应该做什么 - 将 IDisposable
拉到第一个界面或保留原样,并希望用户能够区分一次性和非一次性存储库?
谢谢。
I have the following code example:
public interface IRepository {
// Whatever
}
public class SampleRepository : IRepository {
// Implements 'Whatever'
}
public class NHibernateRepository : IRepository, IDisposable {
// ...
public void Dispose() { ... }
}
Now - is that really bad? I'm not sure, but this seems to be pretty the same as not marking the destructor of the base class virtual in C++
.
I don't want to make the IRepository
interface implement IDisposable
, because that would bring unwanted complexity and bunch of classes which would also have to implement IDisposable
.
How should this case be handled?
I'm sure this can happen to any type hierarchy - when one of the derived type has to manage disposable resources.
So what should I do - pull the IDisposable
up to the very first interface or leave it as it and hope user would distinguish disposable and non-disposable repositories?
Thank you.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
是的,我想说这很糟糕 - 因为你不能以相同的方式使用所有存储库。
我个人会让存储库接口扩展
IDisposable
- 毕竟,用无操作来实现它很容易。这与主框架中的
Stream
、TextReader
和TextWriter
所做的选择完全相同。StringWriter
(例如)不需要处置任何东西......但TextWriter
仍然是一次性的。这一切的发生是因为
IDisposable
在某些方面是一个奇怪的接口:它不向调用者传达提供的东西......它传达的东西调用者的要求(或者至少,如果您不同意,可能会出现问题,强烈建议这样做)。Yes, I'd say this is bad - because you can't use all repositories in the same way.
I would personally make the repository interface extend
IDisposable
- it's easy enough to implement it with a no-op, after all.This is exactly the same choice as is made by
Stream
,TextReader
andTextWriter
in the main framework.StringWriter
(for example) doesn't need to dispose of anything... butTextWriter
is still disposable.This all comes about because
IDisposable
is in some ways an odd interface: it doesn't convey something that is offered to callers... it conveys something which is required of callers (or at least, strongly encouraged with possible issues if you don't go along with it).您可能遇到的唯一问题是您是否使用某种类型的工厂、控制反转或依赖注入模式/框架。如果您想将对象用作接口,您将永远无法做到这一点:
您可能想引入一个实现 IDisposable 的 INHibernateRepository。由于 IDisposable 通常是一个低级操作,因此让您的接口实现此接口并不是一个大问题。
The only issue you might have is if you are using some type of factory, Inversion of Control, or Dependency Injection pattern / framework. If you ever want to use the object as an interface, you will never be able to do this:
You may want to introduce a INHibernateRepository that implements IDisposable. Because IDisposable is usually such a low level operation, there's not a huge issue with making your interfaces implement this interface.
不,不要“拉起 IDisposable”。保持接口原子、简单和独立。
除非您认为 IDisposable 是 IRepository 的基本特征(而 SampleRepository 表明它不是),否则两者之间不应该有任何派生。
No, do not "pull the IDisposable up". Keep interfaces atomic, simple and independent.
Unless you could argue that being IDisposable is a fundamental trait of IRepository (and SampleRepository shows it isn't) it there should be no derivation between the two.
您所描述的内容符合一个重要的附加条件:已将“IDisposable”添加到其基础的类型的对象绝不能传递给使用者,因为使用者可能最终会在未知的时间内保留或保留该对象。
基本上,IDisposable 对象的所有者必须(*) 要么负责处理对象本身,要么将对象移交给可以接受并履行责任的新所有者。最初,IDisposable 对象通常由其创建者“拥有”,但交接很常见。可以将对 IDispoable 对象的引用赋予另一个对象,而无需转移所有权;在这种情况下,所有者仍然有责任在不再需要该对象时处置该对象。为此,所有者必须知道不再需要该对象。最常见的模式是:
如果其中一种模式适用,那么您可能处于良好状态。如果没有,你可能会遇到麻烦。
What you've described is okay with one important proviso: an object of a type which has added 'IDisposable' to its base must never be passed to a consumer which may end up holding or persisting the object for an unknown duration.
Basically, the owner of an IDisposable object must(*) either take care of disposing the object itself, or hand the object off to a new owner which can accept and honor the responsibility. Initially, IDisposable objects are generally "owned" by their creator, but hand-offs are common. A reference to an IDispoable object can be given to another object without transferring ownership; in that scenario, the owner is still responsible for disposing the object when it is no longer needed. For that to happen, the owner has to know the object is no longer needed. The most common patterns are:
If one of those patterns applies, you may be in good shape. If not you may be in trouble.
这对我来说似乎完全没问题。有什么问题吗?
That seems perfectly fine to me. What's the problem?
首先回答你的问题。处置模式与 C++ 析构函数不同。
Dispose
方法旨在处置类包含的资源,而不是处置类本身 em>。将 C++ 析构函数标记为虚拟析构函数的原因在 .NET 中并不存在,因为引用类型的每个实例都有一个包含运行时类型信息的同步块。使用它,垃圾收集器可以适当地回收正确的内存量。
至于使用
IDisposable
扩展IRepository
,这将是一个快速修复,在绝大多数情况下都是可以接受的。我看到的唯一反对意见是扩展接口将要求所有派生类实现该接口。从表面上看,使用 NOP 实现接口似乎很容易(也许多次),但您不必这样做。但是,我可以提供一个替代方案。相反,请考虑使用实现 Dispose 模式的抽象基类。这将遵循处置模式的“标准”实现的结构。
查看 Microsoft 编写的处置模式指南,了解更多信息详细的例子。
First, to answer your question. The dispose pattern is different from a C++ destructor. A
Dispose
method is intended to dispose of resources contained by the class, instead of disposing of the class itself.The reason for marking C++ destructors as
virtual
does not exist in .NET because every instance of a reference type has a sync block which contains run time type information. Using this, the garbage collector can appropriately reclaim the correct amount of memory.As for extending
IRepository
withIDisposable
, that would be a quick fix which would be acceptable in the vast majority of cases. The only objection that I can see is that extending the interface will require all derived classes to implement the interface. On the face, it may seem easy to implement an interface with NOPs (multiple times perhaps), but you shouldn't have to. But, I can offer an alternative.Instead, consider using an abstract base class which implements the Dispose Pattern. This would follow the structure of the "standard" implementation of the dispose pattern.
Take a look at the Dispose Pattern Guidelines written by Microsoft to see a more detailed example.