一文掌握 C# Lazy:延迟加载利器

在日常开发中,你是否遇到过这些问题?
● 程序启动缓慢,因为初始化了大量可能用不到的资源?
● 某个大对象创建耗时,拖累了整个模块的响应速度?
● 想写线程安全的单例,却被双重检查锁搞得头晕眼花?
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 本身有额外的内存和间接访问开销。如果目标对象初始化极其廉价且必然会被使用,直接初始化通常更高效

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code-Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值