在更有效的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();
}
}
}
上面的代码不应该以完全相同的方式运行吗?实际上,驱动程序的生命周期超出了使用块,但是驱动程序在所述块的末尾被处理掉了,原来的代码危险是不是?
答案 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
的许多接口引用,则可能有意义。