C# 使用属性进行延迟初始化的方法

C# 使用属性进行延迟初始化的方法,c#,c#-3.0,recommendation-engine,lazy-initialization,C#,C# 3.0,Recommendation Engine,Lazy Initialization,我目前正在修改一个广泛使用的类,以便将大量昂贵的初始化从类构造函数移到惰性初始化属性中。下面是一个例子(c#): 之前: public class ClassA { public readonly ClassB B; public void ClassA() { B = new ClassB(); } } 之后: public class ClassA { private ClassB _b; public ClassB B

我目前正在修改一个广泛使用的类,以便将大量昂贵的初始化从类构造函数移到惰性初始化属性中。下面是一个例子(c#):

之前:

public class ClassA
{
    public readonly ClassB B;

    public void ClassA()
    {
        B = new ClassB();
    }
}
之后:

public class ClassA
{
    private ClassB _b;

    public ClassB B
    {
        get
        {
            if (_b == null)
            {
                _b = new ClassB();
            }

            return _b;
        }
    }
}
在我正在修改的类中,还有相当多的这些属性,有些属性没有在特定的上下文中使用(因此是惰性的),但是如果它们被使用,它们很可能会被重复调用

不幸的是,这些属性也经常在类内部使用。这意味着私有变量(_b)有可能在不初始化的情况下被方法直接使用

有没有办法只在类中提供公共属性(B),或者在需要时使用相同的方法进行初始化

这是由程序员转载的(显然不够主观):

嗯,我建议的解决方案是告诉您的同事使用属性,而不是字段。但在某种程度上,你可以这样做:

public class ClassA
{
    private Lazy<ClassB> _b = new Lazy<ClassB>(() => new ClassB());

    public ClassB B
    {
        get
        {
            return _b.Value;
        }
    }
}
公共类ClassA
{
private Lazy _b=new Lazy(()=>new ClassB());
公共B类
{
得到
{
返回值;
}
}
}

现在很难拧紧。

可以考虑将惰性属性推到基类中,以避免直接访问后备变量。我知道不太理想。我一直认为这是C#中缺少的东西,即对惰性属性的直接支持。

@chibacity post(随后)删除了一个使用抽象基类的替代选项。虽然它在代码分布方面可能并不理想,但它确实提供了一个很好的封装,消除了大量的代码混乱,使ClassA更干净、更简洁。例如,你可以考虑结合技术来实现这两个目标:

public class ClassB { /* Class to be lazily instantiated */ }

public abstract class BaseA
{
    private Lazy<ClassB> _b = new Lazy<ClassB>(() => new ClassB());
    public virtual ClassB B { get { return _b.Value; } }
}

public class ClassA : BaseA
{
    public override ClassB B { get { return base.B; } }
}
或对于没有无参数构造函数的对象

[Lazy(() => new ClassB("Hello", "World"))]
public ClassB B { get; }
或者像@chibacity在评论中所暗示的那样

public ClassB B { lazyget; }


唉,我认为这些都不是目前可用的任何形式的解决方案…

因为他的第一个示例不是线程安全的,我想他不关心线程安全。我不知道这对性能的影响是什么,除非它们太小了,我只会在一些非常重要的内部循环中担心它。@斯图尔特:编写一个<代码>懒惰< /代码>类太琐碎了,如果你经常写这个模式,你可能会想自己动手做。实例化到基类,并使属性服从于基类属性。这样,同事就不必被告知不要使用该字段,他别无选择,只能使用该属性,而且它具有从ClassA实例中删除无关字段的双重效果。@BenAlabaster:当然,你可以这样做,但我的观点是,目前很难错误地使用它。尝试从字段中获取
ClassB
的唯一方法是调用
.Value
,如果同事这样做了(因为他不知道该属性),那么不管怎样,一切都很好。就我个人而言,我觉得不值得花那么多的精力和代码去做。@Ryan:默认情况下线程不安全的部分是:当两个线程同时“第一次”调用它并竞相初始化它时会发生什么?检查MSDN中Hans提到的
LazyThreadSafetyMode
enum。它解决了这个问题。如果您可以将属性属性设置为惰性,以消除所有这些需要,并在不需要引用.Value的情况下使用隐式惰性加载属性,那就太好了。@BenAlabaster当我第一次看到自动属性时,我认为它们添加了惰性功能,不幸的是没有。如果能够用初始值设定项func定义一个自动属性就太好了。@chibacity有趣的想法,我想知道这是否可以实现。。。开始播放的时间;)@他们肯定会引入一些新的语法和编译器后端。我认为最困难的部分是实现可读性强、易于理解的目标。您可能还必须引入线程配置,除非默认情况下是线程安全的。获得一些清晰的语法是很棘手的
public Bling B{lazyget:new Bling();}
:)@chibacity是否可以通过返回类型和Bling具有无参数构造函数的事实来推断新Bling()?因此,您只需要
public Bling B{lazyget;}
当然,如果您没有无参数构造函数,它将需要类似于您的语法。我喜欢func和属性的语法,但不幸的是,属性参数必须是编译时常量,所以这看起来像是v。不过很好。:)@chibacity这是我在编写API时一直意识到的一件事——在调用或使用此功能时,我希望语法看起来如何?通常,在设计API之前,我会先设计我希望调用的样子,以便一切都正常。@chibacity:谢谢您的回复
public ClassB B { lazyget; }
public ClassB B { lazyget : new ClassB(); }