国庆八天还是有点小快,言归正传,今天给大家介绍一下C#里的延迟初始化,这是我在很久以前面试时曾被问到的一个问题。
延迟初始化
当一个对象第一次被使用时才会创建,就叫延迟初始化。
延迟初始化的作用
延迟初始化的目的是为了提高性能、减少内存需求、避免一些不必要的计算。
延迟初始化的主要应用场景
1、当需要创建一个占用大量内存的对象并且程序可能不会使用它时。
例如,假设在内存中有一个 Customer 对象,该对象具有一个 Orders 属性,该属性包含一个大型 Order 对象数组,要对其进行初始化,需要一个数据库连接。如果用户从不要求显示订单或在计算中使用数据,则没有理由使用系统内存或计算周期来创建它。通过使用 Lazy<Orders> 声明 Orders 对象进行延迟初始化,可以避免在不使用该对象时浪费系统资源。
2、当需要创建成本很高的对象,并且希望把它的创建推迟到其它的关键操作完成之后。
例如,假设您的程序在启动时加载了多个对象实例,但只有其中一些是立即需要的。您可以通过将不需要的对象的初始化推迟到创建所需的对象来提高程序的启动性能。
System.Lazy<T>类
Lazy<T>提供对延迟初始化的支持。
Lazy<T> 内部维护一个状态,用于跟踪对象是否已被初始化:
当首次访问 Lazy<T>.Value 属性时,触发初始化逻辑。
初始化完成后,Lazy<T> 会缓存结果,后续访问 Value 时直接返回已创建的对象,不再重复初始化。
基本使用
1 Lazy<ImageProc> imageProc = new Lazy<ImageProc>();
也可以在声明时传入一个Func<ImageProc>委托生成延迟初始化值
1 Lazy<ImageProc> imageProc = new Lazy<ImageProc>(() => new ImageProc());
说明:
在第一次访问Lazy变量的Value属性之前,不会创建ImageProc的实例。
在第一次访问时,将创建并返回包装的类型,并将其存储以供将来进行任何访问。这也是延迟初始化的核心
线程安全
Lazy<T>提供了一个构造函数,可以控制线程安全特性
var lazyThreadSafe = new Lazy<ImageProc>(() => new ImageProc(),LazyThreadSafetyMode.ExecutionAndPublication);
LazyThreadSafetyMode枚举值 |
|
|
|
1. 非线程安全,多线程同时访问可能导致对象被多次初始化。 2. 适用于单线程场景,性能最优。 |
|
|
1. 完全线程安全,确保只有一个线程执行初始化逻辑,其他线程等待结果。 2. 所有线程最终获取同一个对象实例。 |
|
|
1. 允许多个线程同时执行初始化,但只使用第一个成功创建的对象。 2. 其他线程的初始化结果会被丢弃,适用于允许短暂并行计算的场景。 |
https://docs.microsoft.com/en-us/dotnet/framework/performance/lazy-initialization

