设计困境:谁应该处理一次性参数?

时间:2011-06-24 12:18:44

标签: .net class-design idisposable

如果我的类在构造函数中使用了一次性资源(DbConnection,如果它很重要),我应该在我的类中实现IDisposable并处置DbConnection对象,还是让用户处理{{{ 1}?

目前我在我的班级中实现了IDisposable,但现在我看到了一些可能的负面影响:杂乱的类设计,如果使用不正确,则会DbConnection的双重处理。但也有积极的一面:简化使用是主要的(特别是如果你使用多个一次性参数)。

在“狂野”中我看到了两种方法,所以我无法决定......

更新 感谢大家的答案,事实上,这表明它有时候确实不是一个简单的选择。并且很难选择正确的答案。但是我决定坚持将来最简单的一个。所以最终的选择是:不要实现IDisposable。

4 个答案:

答案 0 :(得分:4)

在构造函数中使用一次性资源是非最佳的,可能会导致异常。您应该在构造函数中注入DbConnectionFactory,该构造函数能够按需创建连接,您可以使用它们并在内部将它们放置在方法中。

如果出于某种原因,这种模式对于您的场景来说是不合逻辑的。下一个最好的选择是仍然将一次性资源的使用从构造函数中移出,并让一个方法返回一个新的类型,这个类型是一次性的并且符合您的目的。

这类似于返回需要从方法处理的数据读取器的服务类型类。 (从理论上讲,它可能是一个真正的数据阅读器)

答案 1 :(得分:4)

它应该由创造它的人处置 - 与其创建的范围相同。你创建一个对象,你负责处理它。就这么简单。

答案 2 :(得分:1)

要么

  1. 未实施IDisposable;调用代码负责处理传入的对象并确保它至少与新对象一样长。
  2. 或者:

    1. 实施IDisposable;调用代码放弃传入对象的所有权,并将其视为已经处置。
    2. 清楚地记录了我做出的选择。有一天,试图让多个地方拥有一次性物品的可能性太大了,恕我直言。

      选项1适用于您打算将一次性对象传递给几个新对象的场景,这些对象将共享对它的访问(数据库连接听起来可能就是这样)。选项2适用于当你正在做的事情更像是在具有更多(或更抽象)功能的另一个对象中包装一次性对象时,特别是如果你要返回它并因此不能控制它的生命周期。

答案 3 :(得分:0)

在某些情况下,对象将使用Disposable资源,其使用寿命超过使用它的新对象的有效寿命。在其他情况下,人们会期望将对象交给一个对其中的任何一次性物品一无所知的实体。某些类型的对象可能会在两种情况下都被使用。

考虑一个假设的IDisposable SoundPlayer类型,该类型将播放IDisposable SoundSource对象,然后在完成后自行处理。很容易想象一个人希望能够加载一次SoundSource,多次播放,然后手动处理它的情况。还可以很容易地想象出一个人想要加载SoundSource对象然后“发射并忘记”玩家的情况。 SoundSource的创建者在播放器完成之前无法处理它,但之后将无法使用它,因此最方便的操作方法是让SoundPlayer处理它。

我建议如果两个场景至少都是合理的,那么你应该提供一个工厂方法来转移IDisposable的所有权以及不能转让IDisposable的所有权,否则你应该有一个带有参数的工厂方法来指示是否所有权应转移。请注意,将IDisposable传递给应该使用的类的构造函数并将它们公开它们是危险的,因为Microsoft不允许从字段初始化程序调用的构造函数周围的try / catch块,并且很难确保在构造函数抛出时处理掉事情。使用工厂方法调用构造函数会使错误捕获变得更容易(尽管它仍然很痛苦 - 尤其是在C#中)。

修改 - 附录的 有时有用的另一种情况方法是让对象发布Disposed事件或在其构造函数中接受委托,以便在不再需要传入的对象时运行。我更喜欢事件的显式委托,因为使用事件意味着事件订阅者(创建临时持有可支配资源的对象的人)有义务取消订阅,这会增加额外的复杂性,特别是如果对象可能是传递到另一个对象。因为到目前为止,当对象的接收者不再需要它时,调用者想要做的最常见的事情就是简单地删除对象,最简单的情况是简单地让对象接收者有一个选项来处理自己的处理