使用泛型和IDisposable指南

时间:2010-11-02 18:44:53

标签: c# c#-4.0

在更有效的C#的第5项中,提供了以下内容:

public class EngineDriver<T> where T : IEngine, new()
{
  public void GetThingsDone()
  {
    T driver = new T();
    using (driver as IDisposable)
    {
      driver.DoWork();
    }
  }
}

这里的目标是正确处理驱动器,如果它实现了IDisposable。这是有道理的,但这种实现与更简洁的方式有何不同:

public class EngineDriver<T> where T : IEngine, new()
{
  public void GetThingsDone()
  {
    using (T driver = new T())
    {
      driver.DoWork();
    }
  }
}

上面的代码不应该以完全相同的方式运行吗?实际上,驱动程序的生命周期超出了使用块,但是驱动程序在所述块的末尾被处理掉了,原来的代码危险是不是?

3 个答案:

答案 0 :(得分:10)

不,因为T不一定实现IDisposable(除非IEngine本身实现它) - 在这种情况下第二个不会编译,而第一个将编译。

关于驱动程序的范围 - 在第二个示例中的使用块之后仍然可以访问它并不理想,并且尝试这样做通常会导致异常。理想情况下,您应IEngine实施IDisposable或向{T}实施EngineDriver添加额外约束。

答案 1 :(得分:7)

使用可用的工具是很重要的。编译您提出的代码。我等几分钟。

好的,你回来了。是的,您需要为IDisposable添加约束,以便using语句始终能够处理该对象。本书中的代码是围绕该限制的黑客,即使T没有实现IDisposable也会有效。 using(null){}有效。

答案 2 :(得分:1)

您的第二个提案仅在IEngine实施IDisposable或者IDisposable上的类型参数中添加了额外约束(EngineDriver)时才有效。原始代码足够灵活,可以另外处理IEngine实现,实现IDisposable

如果您真的担心在处理完对象后使用该对象,您可以创建另一个范围来包含该变量:

public class EngineDriver<T> where T : IEngine, new() {
  public void GetThingsDone() {
    {
      T driver = new T();
      using (driver as IDisposable) {
        driver.DoWork();
      }
    }
  }
}

但是对于这个例子,这是过度的;方法的范围就足够了。只有你的方法更大,才有意义,比如:

public class EngineDriver<T> where T : IEngine, new() {
  public void GetThingsDone() {
    {
      T driver = new T();
      using (driver as IDisposable) {
        driver.DoWork();
      }
    }
    // do more stuff, can't access driver here ....
  }
}

但话又说回来,我只是在这里展示什么是可能的,我更愿意重构它,以便该块与其他代码隔离。

您还可以拥有另一个范围类:

public class DisposableWrapper<T> : IDisposable {
  public T Item { get; private set; }
  public DisposableWrapper(T item) { Item = item; }
  public void Dispose() {
    using (Item as IDisposable) { }
    Item = default(T);
  }
}

public static class DisposableWrapperExtensions {
  public static DisposableWrapper<T> AsDisposable<T>(this T item) {
    return new DisposableWrapper<T>(item);
  }
}

public class EngineDriver<T> where T : IEngine, new() {
  public void GetThingsDone() {
    using (var driver = new T().AsDisposable()) {
      driver.Item.DoWork();
    }
  }
}

如果使用可以实现IDisposable的许多接口引用,则可能有意义。