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

发布于 2025-01-06 19:59:35 字数 629 浏览 4 评论 0原文

嗨,这对我来说似乎是错误的。是这样设计的吗?

我的一次性类:

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

它只会被处理一次。为什么有区别?

Hi this seems wrong to me. Is this the way it was designed?

My disposable class:

class C : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing C");
    }
}

Registration:

cb.RegisterInstance(new C());

Usage:

using (IContainer container = BuildContainer())
{
    var c = container.Resolve<C>();
    Console.WriteLine("C resolved");
}

Output:

C resolved
Disposing C
Disposing C

I think its a bad thing to call Dispose multiple times on the same object.

Note:
When I register the class like this

cb.Register(c => new C());

It gets disposed only once. Why the difference?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

以往的大感动 2025-01-13 19:59:35

我认为对同一个对象多次调用 Dispose 是一件坏事。

事实并非如此,Dispose 应该可以安全地多次调用。从文档中: "如果其 Dispose,则该对象不得引发异常方法被多次调用。” 因为这应该是安全的,所以您不应该依赖其他库只调用它一次,并且您认为不应该产生影响而导致多个 < 的更改没有任何问题。代码>处置来电。

I think its a bad thing to call Dispose multiple times on the same object.

It isn't, Dispose is supposed to be safe to call multiple times. From the documentation: "The object must not throw an exception if its Dispose method is called multiple times." Because this is supposed to be safe, you shouldn't rely on other libraries only calling it once, and there's nothing wrong with changes that you feel shouldn't make a difference causing multiple Dispose calls.

逆夏时光 2025-01-13 19:59:35

检查 AutoFac 源代码揭示了这种双重处理的原因。

RegisterInstance 扩展方法将提供的实例(在本例中为 c)包装在 ProvidedInstanceActivator 中,并将其保存在激活器集合中。当构建器被释放时,所有存储的激活器都会被释放,它们所包含的对象也会被释放(假设它们支持 IDisposable)。

解析的对象(通过 Resolve)也会在 LifetimeScope 容器中进行跟踪,该容器也会在释放构建器时释放其对象。

由于 AutoFac 无法确定解析的对象最初是否是提供的实例,因此这种双重处置仅针对这种类型的注册发生。

这可能是也可能不是错误,具体取决于您的观点,但如果一次性对象如@hvd所述正确编写,则无害。

Examining the AutoFac source code reveals the reason for this double dispose.

The RegisterInstance extension method wraps the provided instance (c in this case) in a ProvidedInstanceActivator, and keeps this in a collection of activators. When the builder is disposed then any stored activators are disposed and so are their contained objects (assuming they support IDisposable).

Resolved objects (via Resolve) are also tracked in a LifetimeScope container which also disposes its objects when the builder is disposed.

Because AutoFac doesn't determine if a resolved object was originally a provided instance then this double dispose occurs for this style of registration only.

This may or may not be a bug, depending on your point of view, but is harmless if the disposable object is written correctly as mentioned @hvd.

梦在深巷 2025-01-13 19:59:35

Hvd 是对的:您应该准备一次性类以允许多个 Dispose 调用。这是实现一次性模式的正确方法,如 MSDN 或 CodeProject

回到原来的问题:

如果组件是 IDisposable (在您的示例中,生命周期范围是容器的生命周期,但它可以是任何其他生命周期范围)。所以这是一种“处置C”。

如果您使用 RegisterInstance 注册了一个组件,那么当容器被释放时,它会调用它们的 Dispose (即使它们从未被解析!)。这是第二个“处置C”。

您可以使用 ExternallyOwned 关闭此额外处理:

builder.RegisterInstance(new C()).ExternallyOwned();

当您使用 cb.Register(c => new C()); 时,Autofac 将创建 C<当您调用 Resolve 时,它会为您提供 /code> 实例,以便它可以跟踪它(它不是“外部拥有”),因此它仅在 litetime 作用域结束时调用一次 Dispose

您可以详细了解 Autofac 的确定性处置

Hvd was right: you should be prepare your disposable class to allow multiple Dispose calls. This is the correct way of implementing the disposable pattern as described in multiple places like MSDN or CodeProject

Back to your original question:

Autofac automatically call Dispose on each component which resolved during a lifetime scope if the component is IDisposable (in your example the lifetime scope is the lifetime of the container but it can be any other life time scope). So this is one "Disposing C".

And if you have registered a component with RegisterInstance then it calls Dispose on them when the container is disposed (even if they are never Resolved!). This is the second "Disposing C".

You can turn this extra dispose off using ExternallyOwned:

builder.RegisterInstance(new C()).ExternallyOwned();

When you used cb.Register(c => new C()); then Autofac creates the C instance for you when you call Resolve so it can track it (it's not "externally owned") so it only calls once Dispose when the litetime scope ends.

You can read more about Autofac's Deterministic Disposal.

习ぎ惯性依靠 2025-01-13 19:59:35

Dispose 模式很容易出错,您需要将其视为潜在的两步操作。

  1. 清除所有分配的非托管资源。 (例如释放内存或调用关闭函数)
  2. 清除所有托管资源。

最常见的方法是我称之为双重处置模式。

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) 方法的内容加锁。

The Dispose pattern is very easy to get wrong, You need to think of it as a potentially two step thing.

  1. Clearing up any allocated unmanaged resources. (eg freeing memory or calling shutdown functions)
  2. Clearing up any managed resources.

The most common way of doing this is what I like to call 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;
        }
    }
}

If your shutdown code needs it, you might have to put a lock around the contents of the Dispose(bool) method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文