在C#中,Lazy<T> 是一个用于实现延迟初始化(Lazy Initialization)的泛型类,主要作用为它包装的类只有在第一次访问时才创建和初始化对象。
适合场景
- 高开销对象
对象创建成本高(如读取大文件、复杂计算、数据库连接)。 - 非必需资源
可能不会被使用的资源(如可选功能、用户未触发的操作)。 - 依赖循环
解决类之间初始化顺序导致的循环依赖问题。 - 线程安全的单例
替代双重检查锁(Double-Check Locking),简化线程安全单例实现。 - 避免启动卡顿
将非关键初始化推迟到程序启动后执行,提升启动速度。
基础语法
| Lazy() | 初始化 Lazy 类的新实例。 发生延迟初始化时,将使用目标类型的无参数构造函数。 |
|---|---|
Lazy(Boolean) |
初始化 Lazy 类的新实例。 发生延迟初始化时,将使用目标类型的无参数构造函数和指定的初始化模式。 |
Lazy(Func) |
初始化 Lazy 类的新实例。 发生延迟初始化时,将使用指定的初始化函数。 |
Lazy(LazyThreadSafetyMode) |
初始化 Lazy 类的新实例,该实例使用 T 的无参数构造函数和指定的线程安全模式。 |
| Lazy(T) | 初始化使用预初始化指定值的 Lazy 类的新实例。 |
| Lazy(Func, Boolean) | 初始化 Lazy 类的新实例。 发生延迟初始化时,将使用指定的初始化函数和初始化模式。 |
| Lazy(Func, LazyThreadSafetyMode) | 初始化使用指定的初始化函数和线程安全模式的 Lazy 类的新实例。 |
Lazy<T> lazyObject = new Lazy<T>(() =>
{
// 初始化逻辑
return new T();
});
-
泛型参数
T:需要延迟初始化的对象类型(如List<int>、Big等)。 -
构造函数:通过委托(通常为 lambda 表达式)定义初始化逻辑。若有无参构造函数,可省略委托直接实例化(可省略委托直接实例化(如
new Lazy<Student>())。Lazy<Student> lazyObject = new Lazy<Student>(); var student = lazyObject.Value;Value属性触发初始化并获取对象实例,后续访问直接返回已缓存的值
支持三种线程安全模式(枚举LazyThreadSafetyMode)
- None:不保证线程安全。适用于单线程场景。
- PublicationOnly:允许多线程竞争初始化,多线程下可能会多次调用 valueFactory,但只保留一次创建的实例。
- ExecutionAndPublication(默认):完全线程安全,确保只调用一次 valueFactory。
注意事项
- 避免循环依赖:
若Lazy<T>的初始化逻辑中嵌套了其他Lazy<T>的访问,可能导致死锁。 - 内存占用:
Lazy<T>本身有额外内存开销,轻量对象可能不值得使用。
常见应用
基础使用
//Lazy<T>()适用无参数构造方法的类
static Lazy<ExpensiveResource> lazyResource = new Lazy<ExpensiveResource>();
static void Main()
{
Console.WriteLine("Start Program.");
// 资源尚未实例化
// 只有第一次访问 .Value 时才会实例化
lazyResource.Value.DoWork();
}
class ExpensiveResource
{
public ExpensiveResource()
{
Console.WriteLine("ExpensiveResource Created!");
}
public void DoWork() => Console.WriteLine("Working...");
}
结果打印
Start Program.
ExpensiveResource Created!
Working...
单例方法
public class Singleton
{
// 使用 Lazy 保证线程安全的延迟初始化
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton(), isThreadSafe: true);
public static Singleton Instance => _instance.Value;
private Singleton() { } // 私有构造函数
}
// 使用
Singleton obj = Singleton.Instance; // 首次访问时创建
注入场景,一个类里有很多方法,但是需要调用这个服务的方法只有一两个,且可能执行不到
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// 注册实际服务
services.AddScoped<IExpensiveService, ExpensiveService>();
// 注册 Lazy 包装器
services.AddScoped(provider =>
new Lazy<IExpensiveService>(() => provider.GetRequiredService<IExpensiveService>())
);
}
// 使用方
public class ConsumerController : Controller
{
private readonly Lazy<IExpensiveService> _lazyService;
public ConsumerController(Lazy<IExpensiveService> lazyService)
{
_lazyService = lazyService; // 注入 Lazy
}
public IActionResult Action()
{
if (condition)
{
// 按需访问(首次访问时初始化)
_lazyService.Value.DoSomething();
}
return View();
}
}
注意
- 明确使用场景
仅对真正可能不被使用且初始化成本高的依赖项使用Lazy<T>,避免滥用。 - 生命周期匹配
- 若依赖项是
Scoped,确保Lazy<T>也是Scoped(而非 Singleton)。 - 若依赖项是
Transient,每次访问Value会创建新实例(需确认是否符合预期)。
- 若依赖项是