【问题标题】:Implementing and using IDisposable实现和使用 IDisposable
【发布时间】:2016-12-16 16:46:00
【问题描述】:

我正在使用实现 IDisposable 的第 3 方对象。为了使其单元测试“能够”,我构建了一个包装器。我知道对象实现了 IDisposable 我的包装器也需要实现 IDisposable。

public interface IWrapper : IDisposable
{
    void Complete();
}

public class Wrapper : IWrapper
{
    private readonly ThirdPartyLib lib;

    public Wrapper()
    {
        lib = new ThirdPartyLib();
    }

    public void Complete()
    {
        lib.Comlete();
    }

    public void Dispose()
    {
        lib.Dispose();
    }
}

public class Processor : IProcessor
{
    private readonly IWrapper wrapper;
    public Processor(IWrapper wrapper)
    {
        this.wrapper = wrapper;
    }

    public void Process()
    {
        // do some work
        using (wrapper) {
            // do more work
        }
    }
}

假设处理器被注入到某个正在使用它的类中并执行 Process()

  • 如果我们再次调用 Process(),包装器会发生什么情况? - ThirdPartyLib() 不会抛出异常,因为它只创建一次(在包装器构造函数中),现在它已被释放
  • 只要有对它的引用,它就不会被释放吗?
  • 是否应该以这样的方式构建包装器:ThirdPartyLib 的 new()“ing”不是在构造函数中而是在单独的方法中执行,比如 Begin() - 像这样:
public class Wrapper : IWrapper
    {
      private ThirdPartyLib lib;

      public void Begin()
      {
          lib = new ThirdPartyLib();
      }

      public void Complete()
      {
          lib.Comlete();
      }

      public void Dispose()
      {
          lib.Dispose();
      }
    }

然后使用它:

using (wrapper.Begin()) {

【问题讨论】:

  • 我觉得我们对此没有大局观。为什么Processor 只处理一个注入构造函数的Wrapper?通常我希望你是Processor 会有一个像Process(IWrapper wrapper) 这样的方法调用,允许你处理那个包装器,但我还是在营地里,调用者必须调用'Dispose' IWrapper 因为它实际上拥有它。如果不是这种情况,那么我可能会在IProcessor 上实现IDisposable,并且仅在处理时处理IWrapper
  • 在 IProcessor 上实现 IDisposable 是否只会在 IProcessor 被释放时释放 Wrapper(在当前设计中),而不是在 using 语句的“}”被命中时释放 Wrapper?这是因为(父) IProcessor 持有对 IWrapper 的引用 - 即使我们告诉它在“}”上进行处理
  • 这就是为什么用 DI 初始化的对象应该假设它们拥有注入的对象。大多数情况下,它们是其他对象使用的单例,或者至少有其他东西在控制生命周期。依赖于 DI 的对象应该只使用该对象并假设。无论创建了传入对象的什么,也应负责稍后对其进行核对。 DI就像“嘿,我只想要一个对象,其他人可以为我创建和删除它”
  • "只要有对它的引用,它就不会被处理掉吗?" -- 我认为一旦不再有对对象的任何强引用,那么它就可以用于垃圾收集。 GC 调用对象的finalizer,GC 不会寻找 Dispose() 并调用它。由于没有更多引用而被垃圾收集的对象与该对象的 Dispose() 方法无关;它们仅在为显式调用 Dispose 的对象实现终结器时才相关。在您的代码中,该对象仅在 调用 Dispose 时“被释放”,这是您在 using 语句中所做的。
  • “只要有对它的引用,它就不会被释放吗?” - 如果你调用Dispose(),那么有问题的对象 i> 无论引用计数如何,都会遭到攻击。

标签: c# asp.net


【解决方案1】:

你的担心是对的。调用Process() 两次可能会导致异常。根本不调用Process() 会使对象未处理。

对于您应该做什么,没有简单的规则。有多个选项,可以使用哪些选项取决于代码中不在您的问题中的其他部分。

如果Processor 引用了外部拥有的Wrapper,那么它根本不应该释放该包装器。

如果Processor 在构造时取得Wrapper 的所有权,那么它应该在处置时处置该包装器。这意味着Processor 也应该实现IDisposable

如果可以修改Processor 以按需创建新的Wrapper,则可以在Process() 完成后处理该包装器。

或者,正如您所建议的,如果可以修改 Wrapper 以按需创建 ThirdPartyLib,那也可以。但要小心:调用wrapper.Begin(); wrapper.Begin(); 会留下一个ThirdPartyLib 未处理和未引用。您需要重新构建您的 API 以防止这成为问题,并且有效地,这意味着将您的 Wrapper 转换为 ThirdPartyLibFactory

【讨论】:

  • 你能解释一下如何调用:wrapper.Begin();包装器。开始();会留下一个未处理的?
  • @BobSwanson wrapper.Begin() 创建第三方库,我们称之为 A。然后你再次调用它,现在你已经创建了第二个第三方库,我们称之为 B。现在你的私有变量 @987654339 @ 指向 B,不再指向 A。当您调用 wrapper.End() 时,它会调用 lib.Dispose() 来处理 B,但 A 仍然存在并且不能再被处理,因为您失去了对它的唯一引用。
【解决方案2】:

我认为Processor 不应该处置IWrapper,因为它不是实例化它的那个,并且它不知道它是否可以被处置或者它是否被注入到其他对象中。在这种情况下,Processor 不应将using 与包装器一起使用。

处理IWrapper 的代码应该在首先实例化它的类中,因为它知道它的生命周期。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-30
    • 2011-01-30
    • 2023-03-28
    • 2011-07-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多