为什么 Lazy<T> 仅限于静态上下文?

问题描述 投票:0回答:3

我想使用 Lazy T 来实现记忆化,但初始化函数似乎需要静态上下文。

例如,以下代码拒绝编译,警告非静态成员ab不可访问。我不清楚为什么会这样,因为 Lazy 对象本身就是一个实例成员,并且在静态上下文中不可见。

public class SomeExpensiveCalculation
{
    private int a;
    private int b;
    public Lazy<int> Result = new Lazy<int>(() => a + b); //nope!
}
c# .net-4.0 lazy-initialization
3个回答
35
投票

构造函数(或方法)外部的对象初始值设定项必须仅引用静态成员。这是因为在构造函数运行之前实例尚未构造,因此字段尚未“准备好”,因此无法引用。静态字段之所以有效,是因为它们在字段之前初始化。

请注意,该错误不是由

Lazy<T>
引起的,而是由 lambda 表达式引起的。解决方法(以及执行此操作的正确方法)是在构造函数中初始化
Result


13
投票

我不知道为什么你的代码不起作用,但这应该可以:

    public class SomeExpensiveCalculation
    {
        private int a;
        private int b;
        public Lazy<int> Result;
        public SomeExpensiveCalculation()
        {
             Result = new Lazy<int>(() => a + b);
        }
    }

2
投票

OP问题中需要注意的一点是,当第一次访问并缓存

a
b
时,将评估
.Value
Result
的值,而不是在
Lazy所在的点
已创建。

这尤其令人困惑,因为

a
b
目前被定义为可变字段。

在下面的示例中,我们展示了

a
b
的值在首次求值时被“冻结”到 Lazy 中(请注意,我已将
a
b
公开用于演示目的) :

public class SomeExpensiveCalculation
{
    public int a;
    public int b;
    public Lazy<int> Result;
    
    public SomeExpensiveCalculation(int a1, int b1)
    {
        a = a1;
        b = b1;
        Result = new Lazy<int>(() => a + b);
    }
}

public void Main()
{
    var x = new SomeExpensiveCalculation(1,2);
    // Result is NOT evaluated
    x.a = 5; // Change the value of a
    Console.WriteLine(x.Result.Value); // 7, i.e. 5 + 2. New value of `a` is used
    x.a = 10;
    Console.WriteLine(x.Result.Value); // Still 7
}

此外,为了扩展@Ondra 的答案,Lazy 也可以与注入工厂一起使用。需要注意的是——要警惕懒惰者和工厂的相对寿命:

public class SomeClass
{
  private readonly Lazy<ISomeDependency> _lazyField;

  // Ctor
  public SomeClass(int a, int b, ISomeFactory factory)
  {
     _lazyField = new Lazy<ISomeDependency>(() => factory.Create(a, b));
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.