作用域和对象释放行为

本文探讨了依赖注入中根容器和子容器的概念,重点解释了DI如何处理实现了IDisposable接口的对象实例释放,强调DI只负责由其创建的实例。建议避免在根容器中获取瞬时服务,并推荐使用容器管理对象生命周期。同时,文章提到了ASP.NET Core中的RequestService和CreateScope创建子容器的方式。总结指出,单例模式在程序关闭时释放,作用域模式在请求结束时释放,而容器只管理实现了IDisposable的服务释放。

一、根容器和子容器

1.根容器

根容器是由通过ServiceCollection Build出的容器。

2.子容器

子容器是由容器创建出来的,有如下两种方式来创建。
第一:RequestService是指ASP.NET Core框架为每个HTTP请求创建的子容器,就是当前请求的容器。
代码如下(示例):

 using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                Debug.WriteLine($"service 's hashcode={service.GetHashCode()}");
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
                Debug.WriteLine($"service1 's hashcode={service1.GetHashCode()}");
            }

第二:使用IServiceprovider的扩展方法CreateScope也可以来创建子容器。

二、实现 IDisposable 接口类型的释放

1.DI 只负责释放由其创建的对象实例

普通方式比如:

services.AddSingleton<IClientService, ClientService>();

工厂注册方式比如:

 var service = new ClientService();
            services.AddSingleton<IClientService>(serviceProvider =>
            {
                return service;
            });

代码如下(示例):
单例:

  /// <summary>
        /// 单例模式,就一个对象,每次都是得到的同一个对象,不会释放;只有程序关闭的时候才会释放
        /// </summary>
        /// <param name="clientService1"></param>
        /// <param name="clientService2"></param>
        /// <returns></returns>
        public int GetSingleServiceLifetime([FromServices] IClientService clientService1, [FromServices] IClientService clientService2)
        {
            using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
            }
            Debug.WriteLine("Done");
            return 0;
        }

Transient:

 /// <summary>
        /// 每次都是一个不同的对象,会释放
        /// </summary>
        /// <param name="clientService1"></param>
        /// <param name="clientService2"></param>
        /// <returns></returns>
        public int GetTransientServiceLifetime([FromServices] IClientService clientService1, [FromServices] IClientService clientService2)
        {
            using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
            }
            Debug.WriteLine("Done");
            return 0;
        }

Result如下:
在这里插入图片描述
Scope:

  /// <summary>
        /// 每个作用域内只能获取相同的对象,作用域释放的时候,再释放对象
        /// </summary>
        /// <param name="clientService1"></param>
        /// <param name="clientService2"></param>
        /// <returns></returns>
        public int GetScopeServiceLifetime([FromServices] IClientService clientService1, [FromServices] IClientService clientService2)
        {
            using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
            }
            Debug.WriteLine("Done");
            return 0;
        }

在这里插入图片描述

2.DI 不负责释放不是由其创建的对象实例

比如注册方式:

services.AddSingleton<IClientService>(new ClientService());

代码如下(示例):

 public int GetServiceLifetime([FromServices]IClientService clientService, [FromServices]IHostApplicationLifetime hostApplicationLifetime,[FromQuery]bool stop =false)
        {
            using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                Debug.WriteLine($"service 's hashcode={service.GetHashCode()}");
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
                Debug.WriteLine($"service1 's hashcode={service1.GetHashCode()}");
            }
            if (stop)
            {
                hostApplicationLifetime.StopApplication();
            }
            Debug.WriteLine($"clientService 's hashcode={clientService.GetHashCode()}");
            Debug.WriteLine("Done");
            return 0;
        }

只有当stop=true,程序关闭的时候才会释放的。

三、建议

1.避免在根容器获取实现了 IDisposable 接口的瞬时服务

代码如下(示例):

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //从根容器获取瞬时服务,容器不会自动释放,若需要释放只能手动
            //var s1 = app.ApplicationServices.GetService<IClientService>();
            //ClientService s2 = (ClientService)app.ApplicationServices.GetService<IClientService>();
            //s2.Dispose();
            //var s3 = app.ApplicationServices.GetService<IClientService>();//在跟容器获取作用域服务会直接报错
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
        }

2.避免手动创建实现了 IDisposable 对象,应该使用容器来管理其生命周期

四、源码下载

下载

总结

  1. AddSingleton支持手动new一个对象注入到容器,这种方式只有程序关闭的时候才去释放,其他两种方式都不支持new一个对象注入容器。
  2. db一般是scope,因为它用完需要释放还给连接池,容器可以帮你释放,service、repository也是scope,因为它们依赖db;单例模式仅在明确需要全局共享一个实例时使用
  3. 容器只会管理实现了IDisposable的服务的释放(调用Dispose方法),对于未实现IDisposable的服务,容器是不管的。另外对象最终的回收,是由GC完成的。生命周期的定义,是决定了容器是返回一个新对象,还是复用之前创建的对象。
  4. IServiceCollection表示定义的服务类型、构造方式和生命周期的配置表,IServiceprovider则表示根容器,当调用GetService时,它会根据IServiceCollection中的定义来决定如何构造(或者返回一个已有的)服务对象。
  5. 单例模式,则程序退出时释放;作用域模式,则每个作用域结束时释放,也就是每个请求结束时。
### QSharedPointer 的作用域结束后的内存管理行为 QSharedPointer 是 Qt 提供的一种智能指针,用于实现共享所有权的内存管理机制。与 C++ 标准库中的 `std::shared_ptr` 类似,QSharedPointer 通过引用计数来跟踪指向同一对象的多个智能指针实例[^3]。当最后一个 QSharedPointer 指向该对象时(即引用计数降为零),它会自动释放所管理的对象以防止内存泄漏。 具体来说,QSharedPointer 在其作用域结束时的行为如下: - **引用计数管理**:每当创建一个新的 QSharedPointer 并将其绑定到一个已有的对象时,引用计数会增加。如果将一个 QSharedPointer 赋值给另一个 QSharedPointer 或者将它超出作用域销毁时,引用计数会减少。 - **自动释放资源**:当引用计数降至零时,表明没有其他 QSharedPointer 指向该对象,此时 QSharedPointer 会调用 `delete` 来释放所管理的对象。 以下是使用 QSharedPointer 的代码示例: ```cpp #include <QSharedPointer> #include <iostream> class MyClass { public: ~MyClass() { std::cout << "MyClass destructor called" << std::endl; } void printMessage() const { std::cout << "Hello from MyClass!" << std::endl; } }; int main() { { QSharedPointer<MyClass> ptr1(new MyClass()); ptr1->printMessage(); QSharedPointer<MyClass> ptr2 = ptr1; // 引用计数加一 ptr2->printMessage(); } // 作用域结束,ptr1 ptr2 都被销毁,引用计数降为零,对象释放 return 0; } ``` 在上述代码中,当 `ptr1` `ptr2` 超出作用域时,引用计数变为零,因此 `MyClass` 的析构函数会被调用以释放资源。 ### 注意事项 尽管 QSharedPointer `std::shared_ptr` 在功能上类似,但它们之间仍存在一些差异。例如,QSharedPointer 不支持自定义删除器,而 `std::shared_ptr` 支持[^2]。此外,QSharedPointer 的性能可能略逊于 `std::shared_ptr`,因为它是为兼容旧版 C++ 标准设计的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值