嗨,这对我来说似乎不对。这是它的设计方式吗?
我的一次性课程:
class C : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposing C");
}
}
注册:
cb.RegisterInstance(new C());
用法:
using (IContainer container = BuildContainer())
{
var c = container.Resolve<C>();
Console.WriteLine("C resolved");
}
输出:
C resolved
Disposing C
Disposing C
我认为在同一个对象上多次调用Dispose是一件坏事。
注意: 当我像这样注册课程时
cb.Register(c => new C());
它只被处置一次。为什么不同?
答案 0 :(得分:12)
我认为在同一个对象上多次调用Dispose是一件坏事。
不是,Dispose
应该是多次安全的。从文档:"The object must not throw an exception if its Dispose method is called multiple times."因为这应该是安全的,你不应该依赖其他只调用一次的库,并且没有任何错误,你认为不应该产生多个{{1调用。
答案 1 :(得分:5)
检查AutoFac源代码揭示了这种双重配置的原因。
RegisterInstance扩展方法将提供的实例(在本例中为c)包装在ProvideInstanceActivator中,并将其保存在激活器集合中。当处理构建器时,则处理任何存储的激活器,并且它们包含的对象也是如此(假设它们支持IDisposable)。
也会在LifetimeScope容器中跟踪已解析的对象(通过Resolve),该容器在处理构建器时也会释放其对象。
由于AutoFac无法确定解析对象最初是否为提供的实例,因此仅对此注册方式进行此双重处理。
根据您的观点,这可能是也可能不是错误,但如果按照@hvd所述正确写入一次性对象则无害。
答案 2 :(得分:2)
Hvd是对的:你应该准备你的一次性课程以允许多个Dispose
电话。这是实现一次性模式的正确方法,如MSDN或CodeProject
回到原来的问题:
如果组件为Dispose
,Autofac会自动调用在生命周期范围内解析的每个组件上的IDisposable
(在您的示例中,生命周期范围是容器的生命周期,但它可以是任何其他生命周期范围)。所以这是一个“处置C”。
如果你已经注册了RegisterInstance
的组件,那么当容器被处理时它会调用Dispose
(即使它们从未被解析过!)。这是第二个“处置C”。
您可以使用ExternallyOwned
:
builder.RegisterInstance(new C()).ExternallyOwned();
当您使用cb.Register(c => new C());
时,当您致电C
时,Autofac会为您创建Resolve
个实例,以便它可以跟踪它(它不是“外部拥有”),因此它只调用一次Dispose
当精简时间范围结束时,{1}}。
您可以阅读有关Autofac Deterministic Disposal的更多信息。
答案 3 :(得分:0)
Dispose模式很容易出错,您需要将其视为潜在的两步。
最常见的做法是我想称之为a double dispose pattern。
public class MyClass : IDisposable {
private bool _disposed = false;
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this); // stop the GC clearing us up,
}
protected virtual Dispose(bool disposing){
if ( !_disposed ){
if ( disposing ){
// someone called Dispose()
// dispose any other IDispose objects we have
}
_disposed = true;
}
}
}
如果您的关机代码需要它,您可能必须锁定Dispose(bool)方法的内容。