属性首次访问时缓存:Lazy<T>使用异常问题咨询
解决Lazy未缓存结果的问题
嘿,我懂你遇到的麻烦了——本来想用Lazy<T>实现“仅在属性被访问时才计算值,且之后缓存结果”的需求,但每次输出都得到不同的时间,说明你的Lazy<T>用法确实出了点问题。
问题根源
大概率是你在属性的getter里每次都创建了新的Lazy<DateTime>实例。举个反例,比如你可能写了这样的代码:
public DateTime ExpensiveValue { get { // 错误:每次访问属性都新建一个Lazy,等于没缓存 var lazy = new Lazy<DateTime>(() => DateTime.Now); return lazy.Value; } }
这种写法下,每次访问ExpensiveValue都会生成一个全新的Lazy实例,自然每次都会重新计算时间,完全起不到缓存作用。
正确的实现方式
你需要把Lazy<T>实例作为类的私有只读字段,只初始化一次;然后属性的getter只返回这个实例的Value属性。这样第一次访问时触发计算,之后所有访问都会直接用缓存的结果。
代码示例:
public class MyDataClass { // 私有Lazy字段:仅在类初始化时创建一次 private readonly Lazy<DateTime> _cachedExpensiveValue = new Lazy<DateTime>(() => { // 这里替换成你的实际高成本获取逻辑(比如数据库查询、远程调用等) Console.WriteLine("正在执行高成本计算..."); return DateTime.Now; }); // 公开属性:返回缓存的值 public DateTime ExpensiveValue => _cachedExpensiveValue.Value; }
测试验证
用下面的代码测试,你会发现只有第一次访问时会打印“正在执行高成本计算...”,两次输出的时间完全一致:
var dataObj = new MyDataClass(); // 第一次访问:触发计算 Console.WriteLine($"第一次访问结果:{dataObj.ExpensiveValue}"); // 等待1秒,模拟时间流逝 System.Threading.Thread.Sleep(1000); // 第二次访问:直接返回缓存结果 Console.WriteLine($"第二次访问结果:{dataObj.ExpensiveValue}");
额外说明
- 默认情况下,
Lazy<T>是线程安全的(使用LazyThreadSafetyMode.ExecutionAndPublication模式),多线程环境下也只会计算一次值。 - 如果你的场景不需要线程安全,可以在初始化
Lazy<T>时指定LazyThreadSafetyMode.None,能稍微提升性能,但要注意线程安全问题。
内容的提问来源于stack exchange,提问作者JohnLBevan
相关产品推荐
相关产品推荐

