简体   繁体   English

使用泛型和IDisposable指南

[英]using guidelines for generics and IDisposable

In item 5 of More Effective C#, the following is presented: 在更有效的C#的第5项中,提供了以下内容:

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

The goal here is to properly dispose of drive, if it implements IDisposable . 这里的目标是正确处理驱动器,如果它实现IDisposable This makes sense, but how is this implementation different from the more succinct: 这是有道理的,但这种实现与更简洁的方式有何不同:

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

Shouldn't the code above behave in exactly the same way? 上面的代码不应该以完全相同的方式运行吗? In fact, isn't the original code dangerous in that the lifetime of driver is beyond the using block, but driver is disposed of at the end of said block? 实际上,驱动程序的生命周期超出了使用块,但是驱动程序在所述块的末尾被处理掉了,原始代码是不是很危险

No, because T does not necessarily implement IDisposable (unless IEngine itself implements it) - the second will not compile in this case, whereas the first will. 不,因为T不一定实现IDisposable (除非IEngine本身实现它) - 第二个不会在这种情况下编译,而第一个将。

Regarding the scope of driver - it will still be accessible after the using block in the second example which is not ideal, and attempting to do so would usually result in an exception. 关于驱动程序的范围 - 在第二个示例中的使用块之后仍然可以访问它并不理想,并且尝试这样做通常会导致异常。 Ideally you should have IEngine implement IDisposable or add an additional constraint to EngineDriver that T implement it. 理想情况下,你应该让IEngine实现IDisposable或者为T实现它的EngineDriver添加一个额外的约束。

It is kinda important that you use the tools that are available to you. 使用可用的工具是很重要的。 Compile your proposed code. 编译您提出的代码。 I'll wait a few minutes. 我等几分钟。

Okay, you're back. 好的,你回来了。 Yes, you need to add a constraint for IDisposable so that the using statement is always capable of disposing the object. 是的,您需要为IDisposable添加约束,以便using语句始终能够处理该对象。 The code from the book is a hack around that restriction, it will work even if T does not implement IDisposable. 本书中的代码是围绕该限制的黑客,即使T没有实现IDisposable也会有效。 using (null) {} is valid. using(null){}有效。

Your second proposal will only work if IEngine implements IDisposable or if an extra constraint (for IDisposable ) is added to the type parameter on EngineDriver . 只有当IEngine实现IDisposable或者在EngineDriver上的type参数中添加了额外约束(对于IDisposable )时,您的第二个提议才有效。 The original code is flexible enough to additionaly handle IEngine implementations that also implement IDisposable . 原始代码足够灵活,可以额外处理实现IDisposable IEngine实现。

If you're really worried about using the object after it's been disposed of, you could create another scope to encompass the variable: 如果您真的担心在处理完对象后使用该对象,您可以创建另一个范围来包含该变量:

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

But for this example, this is overkill; 但是对于这个例子,这是过度的; the scope of the method is enough. 方法的范围就足够了。 It would only make sense if your method was bigger, like: 只有你的方法更大,才有意义:

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

But then again, I'm just showing what's possible here, I'd prefer to refactor it so that that block is isolated from the other code. 但话说回来,我只是在这里展示什么是可能的,我更愿意重构它,以便该块与其他代码隔离。

You can also have another scoping class: 您还可以拥有另一个范围类:

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();
    }
  }
}

It might make sense if you use many interface references that could implement IDisposable . 如果您使用许多可以实现IDisposable接口引用,这可能是有意义的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM