属性加载懒加载

4

我有一个属性,其getter方法应该只在第一次加载其值。第二次调用时,它会返回已加载的值而不会再次加载:

private Object _MemberValue;

public Object MemberValue
{
    get
    {
        if(_MemberValue == null)
        {
            _MemberValue = LoadMember();
        }

        return _MemberValue;
    }
}

在VB.NET中,有一个名为Static的关键字。使用它时,您无需声明类级成员。
Public Property MemberValue as Object
    Get
        Static value as Object = Nothing

        If (value is Nothing) Then
            value = LoadMember()
        End If

        Return value
    End Get
End Property

C# 中没有这样的关键字。

是否有更好的 C# 实现该问题或其他模式?


2
推荐阅读:为什么使用“Static”是不好的。 (https://dev59.com/HlvUa4cB1Zd3GeqPtWxM#7475348) - Patrick Hofman
请查看Lazy对象。 - Nkosi
2
你的第一个代码片段有什么问题吗? - Patrick Hofman
@PatrickHofman 这不是错的,但感觉有更好的实现方式。 - user11909
关于在使用Lazy<>类时的相关问题。https://dev59.com/OGw15IYBdhLWcg3wFHpZ - David Culp
2个回答

10

有没有更好的C#解决方案或其他模式?

可能没有。如果您愿意,可以使用Lazy<T>作为替代,但基本上与第一个示例相同。在VB.NET中使用Static存在一些严重缺点,因此无论如何我都不会使用它。

如果您喜欢Lazy<T>,这就是我会使用的:

private Lazy<object> _MemberLazy = new Lazy<object>(LoadMember);

public object MemberValue
{
    get
    {
        return _MemberLazy.Value;
    }
}

3
嗯,好吧,我想我应该被踩票了,因为我试图帮助你们理解VB.NET的“Static”... - Patrick Hofman
1
有人在这个问题上玩得很开心。显然不喜欢这个问题和答案。尽管如此,我还是给了+1。 - Bugs
我将此标记为答案,因为它回答了我的具体问题。 - user11909

4

您的初始方法似乎是适当的,我从未有过理由去做一些不同的事情。但是,如果您在这里的目标是避免可能会被写入getter外部的类级别字段,也许像这样的东西会起作用。还有其他一些ReadOnly、WriteOnce、SetOnce实现,也可以类似地工作。

ReadOnlyField.cs

public class ReadOnlyField<T>
{
    private bool _frozen;
    private T _value;

    public T Value
    {
        get { return _value; }
        set
        { 
            if (_frozen)
                throw new InvalidOperationException();

            _value = value;
        }
    }

    public void Freeze()
    {
        _frozen = true;
    }
}

YourObject.cs

public class YourObject
{
    private readonly ReadOnlyField<object> _someMember;

    public object MemberValue
    {
        get
        {
            if(_someMember.Value == null)
            {
                _someMember.Value = LoadMember();
                _someMember.Freeze();
            }

            return _someMember.Value;
        }
    }

    public YourObject()
    {
        _someMember = new ReadOnlyField<object>();
    }
}

这并不是完美的。与您的VB.Net示例不同,getter之外的代码可能首先写入该字段,但至少在调用Freeze后,您受到保护免受被覆盖的影响。


2
我没有投票,但我认为这是因为你似乎在复制Lazy<T>的功能,它始于.NET 4。 - the_lotus
@the_lotus 懒惰有其自身的局限性。问题不清楚且不现实。负载可能是其他变量的函数,您可能需要从UI或其他事件进行刷新。 - user6996876

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接