我想使用 Lazy T 来实现记忆化,但初始化函数似乎需要静态上下文。
例如,以下代码拒绝编译,警告非静态成员a和b不可访问。我不清楚为什么会这样,因为 Lazy 对象本身就是一个实例成员,并且在静态上下文中不可见。
public class SomeExpensiveCalculation
{
private int a;
private int b;
public Lazy<int> Result = new Lazy<int>(() => a + b); //nope!
}
构造函数(或方法)外部的对象初始值设定项必须仅引用静态成员。这是因为在构造函数运行之前实例尚未构造,因此字段尚未“准备好”,因此无法引用。静态字段之所以有效,是因为它们在字段之前初始化。
请注意,该错误不是由
Lazy<T>
引起的,而是由 lambda 表达式引起的。解决方法(以及执行此操作的正确方法)是在构造函数中初始化 Result
。
我不知道为什么你的代码不起作用,但这应该可以:
public class SomeExpensiveCalculation
{
private int a;
private int b;
public Lazy<int> Result;
public SomeExpensiveCalculation()
{
Result = new Lazy<int>(() => a + b);
}
}
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));
}
}