延迟初始化的核心是首次访问时才执行高成本操作。适用场景包括创建开销大、使用概率低、依赖时机敏感;.net推荐lazy,支持线程安全与自定义委托;其他语言有类似方案;需注意线程安全、调试困难、访问开销及异常只抛一次等问题。

这正是延迟初始化的核心目的:把高成本操作卡在第一次真正用到它的时候,而不是提前做、白耗资源。
适用场景很明确
不是所有对象都值得延迟初始化。重点看三点:
- 创建开销大——比如要连数据库、加载大文件、启动复杂计算
- 使用概率低——用户可能全程都不点“订单详情”,那Orders对象就根本不用造
- 依赖时机敏感——比如得等主界面渲染完、网络就绪后,再初始化后台服务
.NET 中推荐用 Lazy
它封装了线程安全、异常传播和单次执行逻辑,比手写 if-null-then-create 更可靠:
- 默认构造: Lazy
_orders = new(); —— 首次访问 _orders.Value 才触发无参构造 - 自定义初始化:传入委托,可调用带参构造、加日志、做校验,例如:
new Lazy(() => new Orders(_db, userId)) - 注意:类型若没有无参构造函数,又没传委托,运行时会直接抛异常
其他语言也有对应方案
思路一致,实现方式略有差异:
- Kotlin:只读属性用 by lazy,支持线程模式配置;可变属性用 lateinit(但不带初始化逻辑,也不检查是否已赋值)
- Java(Spring):字段上加 @Lazy,配合 Bean 容器管理生命周期
- JavaScript:用 Proxy 拦截 get,首次访问才 new 实例并缓存,适合图片加载器、远程 API 封装等
小心这些实际坑
延迟初始化不是银弹,落地时得留意:
- 多线程下默认是线程安全的,但自定义委托里如果有共享状态,仍需自行同步
- 调试时看不到初始化时机,建议在委托里加日志或断点
- 每次访问 .Value 都有轻量判断开销,高频调用属性的场景要权衡
- 异常只抛一次——第一次初始化失败,后续再访问仍会重抛同一个异常,不会重试










