为什么autofac会重复处理我的一次性用品?

时间:2012-02-18 11:19:50

标签: c# autofac

嗨,这对我来说似乎不对。这是它的设计方式吗?

我的一次性课程:

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

它只被处置一次。为什么不同?

4 个答案:

答案 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电话。这是实现一次性模式的正确方法,如MSDNCodeProject

等多个地方所述

回到原来的问题:

如果组件为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模式很容易出错,您需要将其视为潜在的两步。

  1. 清除所有已分配的非托管资源。 (例如释放内存或调用关机功能)
  2. 清理所有托管资源。
  3. 最常见的做法是我想称之为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)方法的内容。