在C#开发中,合理管理资源加载是提升应用性能的关键。本文将详细介绍C#中的Lazy<T>
类,这个强大的延迟加载工具能帮助你优化程序执行效率,避免不必要的资源消耗。其实用到这个主要是取近一个老项目迁移,依赖注入有循环注入的,以前听说这个可以,也就试了一下,发现这个可用性还有不少地方。
延迟加载(Lazy Loading)是一种设计模式,核心思想是:只在真正需要时才创建对象或加载资源。在C#中,Lazy<T>
类提供了一种简单优雅的方式来实现这一模式。
Lazy<T>
类封装了一个延迟初始化的对象。以下是基本用法:
C#// 创建Lazy<T>实例
Lazy<ExpensiveObject> lazyObject = new Lazy<ExpensiveObject>(() => new ExpensiveObject());
// 首次访问Value属性时,才会创建ExpensiveObject实例
ExpensiveObject instance = lazyObject.Value;
// 判断是否已经初始化
bool isValueCreated = lazyObject.IsValueCreated;
下面通过一个完整示例来展示Lazy的基本用法:
C#using System;
using System.Threading;
public class Program
{
public static void Main()
{
Console.WriteLine("程序启动...");
// 创建Lazy<T>对象,但不会立即初始化大型资源
Lazy<LargeResource> lazyResource = new Lazy<LargeResource>(() =>
{
Console.WriteLine("正在创建大型资源...");
return new LargeResource();
});
Console.WriteLine("Lazy<T>对象已创建,但大型资源尚未加载");
Console.WriteLine($"资源是否已初始化: {lazyResource.IsValueCreated}");
// 模拟程序运行一段时间
Console.WriteLine("程序执行其他操作...");
Thread.Sleep(2000);
// 只有在首次访问Value属性时,才会执行初始化委托
Console.WriteLine("即将访问资源...");
LargeResource resource = lazyResource.Value;
Console.WriteLine($"资源是否已初始化: {lazyResource.IsValueCreated}");
// 再次访问不会重新创建
Console.WriteLine("再次访问资源...");
resource = lazyResource.Value;
Console.WriteLine("程序结束");
}
}
// 模拟需要大量资源初始化的类
public class LargeResource
{
public LargeResource()
{
// 模拟耗时操作
Thread.Sleep(3000);
Console.WriteLine("大型资源初始化完成!");
}
public void DoWork()
{
Console.WriteLine("资源正在工作...");
}
}
Lazy<T>
提供三种不同的线程安全模式:
C#namespace AppLazyx
{
public class ExpensiveResource
{
private static int counter = 0;
public string Id { get; private set; }
public ExpensiveResource(string mode)
{
// 模拟初始化耗时
Thread.Sleep(100);
Id = $"{mode}-{Interlocked.Increment(ref counter)}";
Console.WriteLine($"创建了资源: {Id}");
}
}
public class Program
{
public static void Main()
{
// 1. ExecutionAndPublication (默认) - 完全线程安全
Lazy<ExpensiveResource> safeLazy = new Lazy<ExpensiveResource>(
() => new ExpensiveResource("完全线程安全模式"),
LazyThreadSafetyMode.ExecutionAndPublication);
// 2. PublicationOnly - 仅发布线程安全
Lazy<ExpensiveResource> publicationLazy = new Lazy<ExpensiveResource>(
() => new ExpensiveResource("仅发布线程安全模式"),
LazyThreadSafetyMode.PublicationOnly);
// 3. None - 不保证线程安全
Lazy<ExpensiveResource> nonSafeLazy = new Lazy<ExpensiveResource>(
() => new ExpensiveResource("非线程安全模式"),
LazyThreadSafetyMode.None);
// 测试多线程访问
TestMultiThreadAccess(safeLazy, "ExecutionAndPublication");
TestMultiThreadAccess(publicationLazy, "PublicationOnly");
}
private static void TestMultiThreadAccess(Lazy<ExpensiveResource> lazy, string mode)
{
Console.WriteLine($"\n测试 {mode} 模式下的多线程访问:");
// 创建多个任务同时访问Lazy对象
Task[] tasks = new Task[5];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() =>
{
Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 正在访问资源...");
ExpensiveResource resource = lazy.Value;
Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 获取到资源: {resource.Id}");
});
}
Task.WaitAll(tasks);
Console.WriteLine($"{mode} 模式测试完成,资源ID: {lazy.Value.Id}\n");
}
}
}
Lazy<T>
会缓存初始化过程中发生的异常,下次访问时会重新抛出:
C#namespace AppLazyx
{
public class ProblematicResource
{
public ProblematicResource()
{
Console.WriteLine("创建有问题的资源...");
}
}
public class Program
{
public static void Main()
{
// 创建可能抛出异常的Lazy对象
Lazy<ProblematicResource> lazyResource = new Lazy<ProblematicResource>(() =>
{
Console.WriteLine("尝试初始化有问题的资源...");
throw new InvalidOperationException("资源初始化失败");
});
try
{
// 首次访问会触发异常
Console.WriteLine("首次尝试访问资源...");
var resource = lazyResource.Value;
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"捕获到异常: {ex.Message}");
}
try
{
// 再次访问会重新抛出缓存的异常,而不会重新执行初始化
Console.WriteLine("\n再次尝试访问资源...");
var resource = lazyResource.Value;
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"再次捕获到异常: {ex.Message}");
}
}
}
}
C#namespace AppLazyx
{
// 使用Lazy实现线程安全的单例模式
public class ConfigurationManager
{
// 私有静态Lazy<T>字段
private static readonly Lazy<ConfigurationManager> _instance =
new Lazy<ConfigurationManager>(() => new ConfigurationManager());
// 公共静态实例属性
public static ConfigurationManager Instance => _instance.Value;
// 配置数据
private readonly string[] _configData;
// 私有构造函数
private ConfigurationManager()
{
Console.WriteLine("加载配置文件...");
// 模拟从文件加载配置
_configData = new string[]
{
"数据库连接串: Server=myserver;Database=mydb;",
"超时设置: 30000",
"最大连接数: 100"
};
Console.WriteLine("配置文件加载完成");
}
public string GetSetting(int index)
{
if (index >= 0 && index < _configData.Length)
return _configData[index];
return "设置不存在";
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("程序启动...");
Console.WriteLine("执行业务逻辑,暂不需要配置...");
// 模拟程序运行一段时间后才需要访问配置
Console.WriteLine("\n需要访问配置,第一次访问将触发ConfigurationManager初始化");
var configManager = ConfigurationManager.Instance;
// 使用配置
Console.WriteLine($"读取配置: {configManager.GetSetting(0)}");
Console.WriteLine($"读取配置: {configManager.GetSetting(1)}");
// 再次获取实例不会重新初始化
Console.WriteLine("\n再次获取ConfigurationManager实例");
var sameManager = ConfigurationManager.Instance;
Console.WriteLine($"是否为同一个实例: {ReferenceEquals(configManager, sameManager)}");
}
}
}
C#using System;
namespace AppLazyx
{
// 服务接口
public interface IEmailService
{
void SendEmail(string to, string subject, string body);
}
// 服务实现
public class EmailService : IEmailService
{
public EmailService()
{
Console.WriteLine("邮件服务初始化中...");
// 模拟复杂的资源初始化
System.Threading.Thread.Sleep(2000);
Console.WriteLine("邮件服务就绪");
}
public void SendEmail(string to, string subject, string body)
{
Console.WriteLine($"发送邮件到: {to}");
Console.WriteLine($"主题: {subject}");
Console.WriteLine($"内容: {body}");
}
}
// 使用延迟加载的服务容器
public class ServiceContainer
{
// 使用Lazy延迟初始化服务
private readonly Lazy<IEmailService> _emailService =
new Lazy<IEmailService>(() => new EmailService());
// 暴露延迟加载的服务
public Lazy<IEmailService> EmailService => _emailService;
}
// 演示类
public class DependencyInjectionDemo
{
public static void Main()
{
// 创建服务容器
var container = new ServiceContainer();
Console.WriteLine("服务容器已创建,但服务尚未初始化");
// 创建使用服务的组件
var userRegistration = new UserRegistration(container.EmailService);
Console.WriteLine("用户注册组件已创建");
// 进行不需要发送邮件的操作
userRegistration.ValidateUser("user@example.com", "password");
// 只有在需要发送欢迎邮件时才会初始化邮件服务
Console.WriteLine("\n准备发送欢迎邮件...");
userRegistration.SendWelcomeEmail("user@example.com");
}
}
// 使用延迟加载服务的组件
public class UserRegistration
{
private readonly Lazy<IEmailService> _emailService;
public UserRegistration(Lazy<IEmailService> emailService)
{
_emailService = emailService;
Console.WriteLine("用户注册组件已初始化,尚未加载邮件服务");
}
public bool ValidateUser(string email, string password)
{
Console.WriteLine($"验证用户: {email}");
// 此操作不需要邮件服务
return true;
}
public void SendWelcomeEmail(string email)
{
Console.WriteLine($"准备向 {email} 发送欢迎邮件");
// 只有在这里才会初始化邮件服务
_emailService.Value.SendEmail(
email,
"欢迎加入我们!",
"感谢您的注册,这里是一些使用说明..."
);
Console.WriteLine("欢迎邮件已发送");
}
}
}
C#using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace AppLazyx
{
public class PerformanceDemo
{
public static void Main()
{
const int iterations = 10000;
Console.WriteLine("性能对比:直接加载 vs. 延迟加载\n");
// 测试直接加载
Stopwatch sw = Stopwatch.StartNew();
List<DataProcessor> directProcessors = new List<DataProcessor>();
for (int i = 0; i < iterations; i++)
{
directProcessors.Add(new DataProcessor());
}
sw.Stop();
Console.WriteLine($"直接创建 {iterations} 个处理器用时: {sw.ElapsedMilliseconds}ms");
// 测试延迟加载但不访问
sw.Restart();
List<Lazy<DataProcessor>> lazyProcessors = new List<Lazy<DataProcessor>>();
for (int i = 0; i < iterations; i++)
{
lazyProcessors.Add(new Lazy<DataProcessor>());
}
sw.Stop();
Console.WriteLine($"创建 {iterations} 个Lazy<T>包装器用时: {sw.ElapsedMilliseconds}ms");
// 测试访问Lazy包装对象的值
sw.Restart();
foreach (var processor in lazyProcessors)
{
DataProcessor instance = processor.Value;
}
sw.Stop();
Console.WriteLine($"访问 {iterations} 个Lazy<T>对象的值用时: {sw.ElapsedMilliseconds}ms");
// 内存使用比较
Console.WriteLine("\n内存占用比较 (仅供参考):");
GC.Collect();
GC.WaitForPendingFinalizers();
long beforeSize = GC.GetTotalMemory(true);
// 创建大量未初始化的Lazy对象
List<Lazy<LargeData>> lazyData = new List<Lazy<LargeData>>();
for (int i = 0; i < 1000; i++)
{
lazyData.Add(new Lazy<LargeData>());
}
long afterLazySize = GC.GetTotalMemory(true);
Console.WriteLine($"1000个未初始化的Lazy<LargeData>占用内存: {(afterLazySize - beforeSize) / 1024}KB");
// 创建同等数量的直接实例
List<LargeData> directData = new List<LargeData>();
for (int i = 0; i < 1000; i++)
{
directData.Add(new LargeData());
}
long afterDirectSize = GC.GetTotalMemory(true);
Console.WriteLine($"1000个直接创建的LargeData占用内存: {(afterDirectSize - afterLazySize) / 1024}KB");
}
}
// 模拟数据处理器
public class DataProcessor
{
public DataProcessor()
{
// 模拟轻量级初始化
}
}
// 模拟大数据对象
public class LargeData
{
// 模拟大量数据
private byte[] _data = new byte[10000];
}
}
这个对有些注入依赖时用处挺大的,但最好不要这么干 。
C#using System;
// 演示循环依赖问题
public class CircularDependencyDemo
{
public static void Main()
{
try
{
Console.WriteLine("尝试创建有循环依赖的对象...");
// 创建A和B,它们互相引用
Lazy<ComponentA> lazyA = null;
Lazy<ComponentB> lazyB = null;
lazyA = new Lazy<ComponentA>(() => new ComponentA(lazyB));
lazyB = new Lazy<ComponentB>(() => new ComponentB(lazyA));
// 尝试访问A,这将触发循环依赖
Console.WriteLine("尝试访问组件A...");
var a = lazyA.Value;
// 这行代码永远不会执行,因为上面会抛出异常
Console.WriteLine("成功创建组件!");
}
catch (Exception ex)
{
Console.WriteLine($"发生异常: {ex.GetType().Name}");
Console.WriteLine($"异常信息: {ex.Message}");
if (ex.InnerException != null)
{
Console.WriteLine($"内部异常: {ex.InnerException.Message}");
}
Console.WriteLine("\n解决方案:重新设计对象关系,避免循环依赖,或使用接口注入");
}
}
}
public class ComponentA
{
private readonly Lazy<ComponentB> _b;
public ComponentA(Lazy<ComponentB> b)
{
Console.WriteLine("创建组件A");
_b = b;
// 访问B将触发循环依赖
Console.WriteLine($"组件A尝试访问组件B...");
var temp = _b.Value;
}
}
public class ComponentB
{
private readonly Lazy<ComponentA> _a;
public ComponentB(Lazy<ComponentA> a)
{
Console.WriteLine("创建组件B");
_a = a;
// 访问A将触发循环依赖
Console.WriteLine($"组件B尝试访问组件A...");
var temp = _a.Value;
}
}
Lazy<T>
是C#中实现延迟加载的强大工具,可显著优化应用性能和资源使用。本文通过详细示例展示了其基本用法、线程安全模式、异常处理以及实际应用场景。掌握Lazy<T>
的使用技巧,让你的C#应用更高效、更可靠。
希望这篇详解对你有所帮助!如有问题,欢迎在评论区留言交流。
关键词:C# Lazy、延迟加载、懒加载、Lazy<T>、性能优化、C#单例模式、线程安全
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!