在using块中使用“as IDisposable”

3

编辑:我的问题不是关于使用块以及它的工作原理。我的问题是关于下面两种方法之间的区别。

我正在阅读CQRS旅程指南,我不理解这行代码:

using (repo as IDisposable)

这是什么意思?为什么要将其用作IDisposable?在典型的using块中,人们不需要将其用作IDisposable:
using (var repo = this.respositoryFactory()) { // ... }

你有没有想过为什么作者用第一种方式而不是第二种方式编写代码?

以下是该代码的方法:

private Conference.Web.Public.Models.Conference GetConference(string conferenceCode)
{
    var repo = this.repositoryFactory();
    using (repo as IDisposable)
    {
        var conference = repo.Query<Conference>()
            .First(c => c.Code == conferenceCode);
        var conferenceModel =
        new Conference.Web.Public.Models.Conference
        {
            Code = conference.Code,
            Name = conference.Name,
            Description = conference.Description
        };
        return conferenceModel;
    }
}

简单来说,在using块中你不能使用任何旧类型,它必须是一个IDisposable类型。 - iamkrillin
3
如果一个类没有实现IDisposable接口,为什么使用as IDisposable就可以神奇地允许这个实例被处理?如果这个类已经实现了IDisposable,那么为什么不能直接在using语句中使用它而需要进行类型转换呢?我从来没有看到过需要这样做。 - Jeroen Vannevel
1
@iamkrillin 如果某个对象已经是可处理的(disposable),则不必在 using 块中将其设置为“as IDisposable”。 - Bob Horn
1
简而言之,强制类型转换是不必要的。 - iamkrillin
2
这里有一个很好的解释:https://dev59.com/wG445IYBdhLWcg3wytIU - sakura-bloom
显示剩余4条评论
3个回答

10
这通常是由于对语言特性的误解导致的。你写的方式是 C# 中惯用的写法:
using (var repo = this.respositoryFactory()) 
{
作者的方法唯一真正的优势在于,如果repositoryFactory可能返回一个未实现IDisposable接口的类型。通过使用using (repo as IDisposable)编写代码,同样的代码可以处理非可释放类型。
大多数情况下,这不是问题。但是,工厂可能有选择地返回IDisposable类型。例如,假设此方法在泛型类中完成,repositoryFactory返回IRepository<T>,该类型可能会实现或不实现IDiposable接口,在这种情况下,这种方法将处理这两种情况,而不会对泛型类型施加IDisposable约束。

那不是假设repositoryFactory返回实现了IDisposable的类型吗?如果它返回一个object呢? - CodeNaked
3
repositoryFactory 必须返回一个 IDisposable 接口,否则将无法通过编译。 - Bob Horn
@JeroenVannevel - 抱歉,你是正确的,如果你不进行强制转换,那么它将会是一个编译器错误。但是强制转换的目的是说,如果它实现了IDisposable接口,那么在using语句中就将其释放。 - CodeNaked
1
如果您说using m=this.repositoryFactory() as IDisposable,那么如果repositoryFactory()返回的对象未实现IDisposable,则m将为null。在最初编写的代码中,repo将持有一个不实现IDisposable的对象的引用,而using块将完美地保护null - supercat
问题不仅限于泛型类型。工厂方法声明的返回类型如果未实现IDisposable,仍然可以创建需要清理的实现了IDisposable接口的对象类型的实例。非泛型IEnumerable.GetEnumerator()也是如此。我认为这种工厂方法是一种极端反模式,但既然这种东西存在,使用它们的代码必须尽力而为,并且在原始代码中显示的模式可能是最佳选择。 - supercat
显示剩余6条评论

3
使用using(X as IDisposable)语法指示编译器,X代表实现IDisposable接口的特定类型的实例,然后using块将保护该实例,否则不会保护任何内容。如果方法this.repositoryFactory()的返回类型不实现IDisposable,但该方法有时会返回实现IDisposable的类型的实例并期望调用它,则可以使用此用法。
请注意,任何作为可能生成需要清理的IDisposable对象工厂的返回类型的类型都应实现IDisposable,即使只有99.9%的生成对象具有Dispose方法,并且实际上不必调用。非泛型IEnumerator完全符合该描述,因此应该实现IDisposable,但由于它没有实现,因此最好的使用模式可能是:
IEnumerator enumerator = myEnumerable.GetEnumerator();
using (enumerator as IDisposable)
{
  ...
}

非常类似于使用上面的 repositoryFactory() 代码中观察到的情况。

0

如果在using块的某个地方代码出错或抛出异常,那么保证repo已被处理。

两行代码之间的区别是没有区别的。转换为IDisposable不是必要的,纯粹是理论上的可能差异

  • 在使用后您想对repo执行某些操作。
  • 更清晰的代码

1
我明白了。这对任何一种方法都是这样吗?两者之间有什么区别? - Bob Horn
如果DepositoryFactory的返回类型没有实现IDisposable,但该方法有时可能生成实现了IDisposable的类型的实例,并且因此需要清理,则需要使用as IDisposable - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接