You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用DI/IoC容器时如何处理对象销毁?以DryIoc为例

单例服务中按需创建并销毁含非托管资源服务的解决方案

问题描述

我正在使用DryIoc,但这是一个所有IoC容器通用的问题。假设有一个单例服务SingletonService,它每次调用方法时都需要一组新对象(这里简化为WorkerService),该服务使用非托管资源,因此必须确保销毁。

我不清楚如何确保WorkerService在不再需要时立即销毁。据我了解,在单例服务中持有容器引用属于服务定位器反模式,不可取。

我想到的一种解决方案是向SingletonService注入WorkerServiceFactory,该工厂通过注入获取容器,再用容器获取WorkerService并返回给SingletonService。但我不确定是否应由SingletonService负责销毁WorkerService?是否有准则指出容器创建的对象应由容器销毁?另外我也考虑使用作用域,但不确定如何实现:如果在SingletonService中创建新作用域,仍会持有容器引用(至少DryIoc中是如此);在工厂中创建作用域则意义不大,因为只有工厂能访问该作用域。

请问该如何处理这类场景?

补充实现方案

我进一步思考后提出了如下实现:

using DryIoc;
using JetBrains.Annotations;

using var container = new Container();
container.Register<ISingletonService, SingletonService>(reuse: Reuse.Singleton);
container.Register<IWorkerService, WorkerService>(reuse: Reuse.Scoped);
container.Register<IWorkerServiceFactory, WorkerServiceFactory>(reuse: Reuse.Singleton);
var singleton = container.Resolve<ISingletonService>();
singleton.PrintData();
singleton.PrintData();
singleton.PrintData();
singleton.PrintData();
singleton.PrintData();


interface ISingletonService
{
    void PrintData();
}

class SingletonService(IWorkerServiceFactory workerFactory) : ISingletonService
{
    public void PrintData()
    {
        using var scope = workerFactory.Create(out var worker);
        Console.WriteLine(worker.GetData());
    }
}

interface IWorkerService
{
    string GetData();
}

class WorkerService : IWorkerService, IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("WorkerService was disposed");
    }

    public string GetData()
    {
        return "Hello Data";
    }
}

interface IWorkerServiceFactory
{
    [MustDisposeResource]
    IDisposable Create(out IWorkerService service);
}

class WorkerServiceFactory(IResolverContext ctx) : IWorkerServiceFactory
{
    public IDisposable Create(out IWorkerService service)
    {
        var scope = ctx.OpenScope();
        service = scope.Resolve<IWorkerService>();
        return scope;
    }
}

本质上我通过out参数返回WorkerService,同时将工厂内创建的作用域返回给调用方。这样SingletonService只需销毁工厂返回的作用域,其余由容器处理。请问这是一种合理的方案吗?

方案分析与优化建议

你的方案是合理且符合IoC最佳实践的,核心思路解决了几个关键问题:

  • 避免服务定位器反模式:单例服务仅依赖工厂接口,不直接持有容器引用
  • 确保资源及时释放:通过返回作用域并让调用方用using声明管理,销毁作用域时容器会自动处置该作用域内所有实现IDisposable的服务(包括WorkerService)
  • 遵循容器管理准则:容器创建的对象由容器负责销毁,这里通过作用域销毁触发容器清理逻辑,完全符合规范

可优化点

  1. 替换out参数为元组返回:C# 7.0+支持元组返回,代码可读性更好,无需依赖out参数:
    interface IWorkerServiceFactory
    {
        [MustDisposeResource]
        (IDisposable Scope, IWorkerService Service) Create();
    }
    
    class WorkerServiceFactory(IResolverContext ctx) : IWorkerServiceFactory
    {
        public (IDisposable Scope, IWorkerService Service) Create()
        {
            var scope = ctx.OpenScope();
            var service = scope.Resolve<IWorkerService>();
            return (scope, service);
        }
    }
    
    // 调用方代码
    public void PrintData()
    {
        var (scope, worker) = workerFactory.Create();
        using (scope)
        {
            Console.WriteLine(worker.GetData());
        }
    }
    
  2. 添加作用域名称(可选):给工厂创建的作用域添加名称,方便调试和跟踪生命周期:
    var scope = ctx.OpenScope("WorkerServiceScope");
    

简化替代方案

如果使用DryIoc,可利用委托注入省去手动编写工厂类的代码,同时保持同样的生命周期管理能力:

// 注册创建作用域和服务的委托
container.RegisterDelegate<Func<(IDisposable, IWorkerService)>>(ctx =>
{
    return () => 
    {
        var scope = ctx.OpenScope();
        return (scope, scope.Resolve<IWorkerService>());
    };
}, Reuse.Singleton);

// 单例服务直接依赖该委托
class SingletonService(Func<(IDisposable, IWorkerService)> workerFactory) : ISingletonService
{
    public void PrintData()
    {
        var (scope, worker) = workerFactory();
        using (scope)
        {
            Console.WriteLine(worker.GetData());
        }
    }
}

内容的提问来源于stack exchange,提问作者patvax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 01:16:12