一、前言

前面写过一篇博客,是关于Ioc控制反转的。
那篇文中,主要介绍了Ioc思想及其常见实现方式——依赖注入(DI),然后演示了 Microsoft.Extensions.DependencyInjection 的使用(这个应该算.NET自带的DI包)。

不过DI的使用上,只是简单演示流程,并未进行深入学习。本文就DI中的 服务生命周期(ServiceLifetime) 进一步学习。


二、服务生命周期

2.1 服务生命周期

为什么会从服务生命周期学起呢?
因为我往服务集(ServiceCollection)中添加元素时,发现有三种方法:
AddScoped()、AddSingleton()、AddTransient()。
分别表示范围的/作用域的、单例的、瞬时的,而这三种方法对应ServiceLifetime枚举中的三个字段。

    public enum ServiceLifetime
    {
        //
        // 摘要:
        //    将服务创建为单例
        Singleton,
        //
        // 摘要:
        //     为每个作用域创建新的服务实例
        //
        // 言论:
        //     In ASP.NET Core applications a scope is created around each server request.
        Scoped,
        //
        // 摘要:
        //		每次都会创建一个新实例
        //             //     requested.
        Transient
    }
Singleton
对象在整个应用程序生命周期中只被创建一次,所有请求都共享这个实例。适用于全局共享的资源或服务,如配置、缓存等。
Scoped
在同一个作用域内,对象只会被创建一次,所有请求都共享这个实例。当作用域结束时,对象会被销毁。适用于需要在一个请求周期内共享状态的场景。
Transient
每次请求都会创建一个新的对象实例,不同请求之间相互独立。适用于无状态的服务或短暂的对象。

这些策略有助于控制资源的使用和提高应用程序的性能。

2.2 状态

这里的“状态”指的是。状态可以是对象的属性或字段值,用于描述对象在某个特定时间点的状况。有状态的对象在其生命周期内会发生变化,而无状态的对象则在整个生命周期内保持不变。在依赖注入的上下文中,选择合适的生命周期管理策略(Scoped、Singleton、Transient)可以确保对象在其生命周期内正确地维护其状态。

2.3 拓展:静态类和单例的应用场景

说到状态,再进一步拓展一下。

上面说了无状态对象在整个生命周期内保持不变。一个典型的例子就是一个计算器类。计算器类可以包含一些用于执行基本数学运算(如:加减乘除)的方法,但它不会存储任何与这些操作相关的数据。每次调用计算器方法时,只需根据传入的参数执行相应的计算并返回结果,而不需要保持或修改任何内部状态(当然,也有有历史记录的计算器,计算后会修改其状态,但这边不讨论该情况)。因此,计算器类是无状态的,可以在多个请求之间共享和重用,而不会产生副作用或竞争条件。

对于无状态类,可以使用静态类或单例模式,具体取决于你的需求和设计目标。

静态类(static class)
静态类中的方法和属性都是静态的,这意味着你无需创建类的实例就可以直接访问它们。静态类适用于不需要维护状态的实用程序方法或功能,如数学计算、字符串操作等。静态类的静态成员只会被创建和初始化一次,然后在整个应用程序中共享,因此在内存占用方面较为节省。
单例模式
单例模式确保一个类在程序中仅有一个实例,并提供一个全局访问点。它适用于需要维护状态但状态可在整个应用程序中共享的情况。虽然单例模式也可用于无状态类,但通常静态类更为简单、高效。

对于无状态类,静态类通常是更好的选择,因为它们简单、高效且易于使用。

三、总结

在Ioc中,

  • Singleton用于在整个应用程序生命周期中只创建一次,每次请求都是同一个实例的情况;
  • Scoped用于同一作用域内只被创建一次;
  • Transient每次请求都是一个新的实例;
  • 状态是对象在其生命周期内保持和维护的数据或信息;
  • 无状态类往往用静态类实现。
05-09 14:31