在日常开发中,你是否遇到过这些问题?
● 程序启动缓慢,因为初始化了大量可能用不到的资源?
● 某个大对象创建耗时,拖累了整个模块的响应速度?
● 想写线程安全的单例,却被双重检查锁搞得头晕眼花?
Lazy 正是 C# 为解决这类问题而生的优雅方案!它通过 “延迟初始化”** 的核心机制,将对象的创建推迟到第一次真正被访问的时刻,从而显著提升程序效率与资源利用率。今天,我们就来深入探讨下这个性能优化的利器。
一、Lazy是什么?
我们先看看官方的解释

Lazy本质上来说是一个泛型类,他有着延迟创建的特性,只有当代码首次访问其.Value属性时,Lazy才会症状的调用创建目标对象
// 1. 创建 Lazy<T> 实例,指定如何创建 ExpensiveResource (此时并未创建!)
Lazy<ExpensiveResource> lazyResource = new Lazy<ExpensiveResource>(() =>
{
Console.WriteLine("正在创建昂贵的资源...");
return new ExpensiveResource(); // 模拟高成本操作
});
Console.WriteLine("Lazy 对象已创建,资源未初始化: " + !lazyResource.IsValueCreated);
// 2. 首次访问 .Value,触发初始化!
ExpensiveResource resource = lazyResource.Value; // 输出"正在创建昂贵的资源..."
resource.DoWork();
// 3. 后续访问,直接获取缓存对象
ExpensiveResource sameResource = lazyResource.Value; // 无输出,直接返回
Console.WriteLine("资源已初始化: " + lazyResource.IsValueCreated);
二、Lazy的核心使用场景
1.优化高开销对象初始化:
对象创建成本极高,避免在程序启动或对象构造时就承担不必要的开销
2.需加载非必需资源:
某些资源或功能可能根本不会被使用,完全避免初始化那些最终未被使用的资源
3.实现安全的单例模式:
安全机制下实现懒汉单例
代码实现
public class Singleton
{
// 私有静态 readonly Lazy 实例
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton());
// 公有静态访问点
public static Singleton Instance => _instance.Value;
// 私有构造函数
private Singleton()
{
Console.WriteLine("Singleton实例被创建!");
}
}
// 使用:Singleton obj = Singleton.Instance; // 首次访问时创建
在代码实现中我们需要注意,在单例情况下声明Lazy应该指定其构造函数
正确写法:
private static readonly Lazy<Singleton> _instance = new Lazy<Singleton>(() => new Singleton());
错误写法:不指定其构造函数
private static readonly Lazy<Singleton> _instance = new Lazy<Singleton>();
在我们不指定构造函数,且构造函数私有的情况下,将产生如下报错

三、Lazy的多线程守护方式
Lazy 的强大之处在于其内置的、可配置的线程安全机制,这是手动实现难以比拟的优势。通过 LazyThreadSafetyMode 枚举,你可以精确控制其行为
1. LazyThreadSafetyMode.ExecutionAndPublication (默认且最常用)
- 机制:完全线程安全。采用类似优化的双重检查锁或更高效的机制。
- 保证:有且只有一个线程会执行初始化委托 (valueFactory),并且所有线程看到的 .Value 都是同一个、完全初始化好的对象。
- 适用:绝大多数需要多线程共享 Lazy 实例的场景。单例模式的标准选择。
2. LazyThreadSafetyMode.PublicationOnly
- 机制:仅保证发布结果的线程安全。允许多个线程并发执行初始化委托。
- 保证:第一个完成初始化的线程将其结果发布给所有等待的线程,后续线程丢弃自己创建的结果(即使创建了也白创建)。最终所有线程看到的 .Value 是第一个成功创建的对象。初始化委托可能被执行多次!
- 适用:初始化逻辑本身完全线程安全、无副作用且可重复执行,并且你能接受潜在的多余初始化开销。追求更高的并发吞吐量时考虑。
3. LazyThreadSafetyMode.None
- 机制:无任何线程安全保证。
- 风险:多线程环境下访问 .Value,可能导致初始化委托被多次执行(产生多个对象实例),或者线程看到尚未完全初始化完成的对象。
- 适用:绝对单线程环境,或者你能百分百确保该 Lazy 实例只会在一个线程中被访问。性能最高,但风险最大。
最佳实践: 除非有明确理由和充分把握,否则优先使用默认的 ExecutionAndPublication 模式。它能安全地处理绝大多数并发场景
四、何时使用Lazy?
当你的代码面临,如下问题时,我们可以考虑使用Lazy关键字
- “启动慢”
- “资源吃紧”
- “单例安全难”
- “服务加载烦”
当然Lazy也不是万能的,Lazy 本身有额外的内存和间接访问开销。如果目标对象初始化极其廉价且必然会被使用,直接初始化通常更高效
1426

被折叠的 条评论
为什么被折叠?



