懒散地评估属性语法差异



对于延迟求值的属性,以下两种方法(带支持字段的属性和带默认值的属性)之间有什么区别吗?或者它们是等效的?

// (1)
public static class Foo
{
private static readonly Lazy<Foo> instance = new Lazy<Foo>();
public static Foo Instance { get; } = instance.Value;
}
// (2)
public static class Foo
{
public static Foo Instance { get; } = new Lazy<Foo>().Value;
}

我想实现的是,Foo的实例只在访问Foo.Instance时创建,而不是在访问之前创建——更重要的是,当Foo.Instance从未被访问时,就不应该创建任何实例。

实际上,不,它们没有什么不同。

但是,这只是一个假设,请注意,它们也不起作用,至少不是我希望你想要的那样

你看,这个语法:

<property declaration> = <expression>;

声明属性的初始值设定项,它将在所属类型的构造时执行。

所以这个:

private static readonly Lazy<Foo> instance = new Lazy<Foo>();
public static Foo Instance { get; } = instance.Value;

一点也不懒。它将声明并构造一个Lazy<Foo>(尽管在编译时可能也缺少getter委托),但是当您声明属性时,您最终会得到在构造所属类型时评估惰性对象的属性,因此它变得非惰性。

第二个有完全相同的问题,您构建并立即评估懒惰对象,使其成为非懒惰对象。

正确的方法,这只能在第一种语法形式中实现,是在没有初始化器的情况下使用属性,或者这样:

private static readonly Lazy<Foo> instance = new Lazy<Foo>();
public static Foo Instance
{
get { return instance.Value; }
}

或者这个:

private static readonly Lazy<Foo> instance = new Lazy<Foo>();
public static Foo Instance
{
get => instance.Value;
}

或者可能是最好的,如下:

private static readonly Lazy<Foo> instance = new Lazy<Foo>();
public static Foo Instance => instance.Value;

这将声明一个getterbody,直到您实际读取该属性时才会执行。


TL;DR总之,您给出的两个示例没有什么不同,但它们都(可能)是错误的,您需要更改属性声明来修复它。

除了Lasse Vågsæther Karlsen给出的出色答案外,我想进一步推测并假设OP是在Singleton实现之后。这是一种完全懒惰的方法。

Approach 1
public sealed class Singleton {
//Private ctor of course :P
private Singleton() {}
// Instance property to access Singleton Instance
public static Singleton Instance { get { return Nested.instance; } }
private class Nested {
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested() { }
internal static readonly Singleton instance = new Singleton();
}
}
Approach 2
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance { get { return lazy.Value; } }
private Singleton()
{
}
}