实现“LazyProperty”类 - 这是一个好主意吗?

时间:2009-01-13 18:59:14

标签: c# .net lazy-evaluation

我经常发现自己正在编写一个懒惰评估的属性。类似的东西:

if (backingField == null) 
  backingField = SomeOperation();
return backingField;

代码不多,但如果你有很多属性,它会重复很多。

我在考虑定义一个名为LazyProperty的类:

public class LazyProperty<T>
    {
    private readonly Func<T> getter;

    public LazyProperty(Func<T> getter)
    {
        this.getter = getter;
    }

    private bool loaded = false;
    private T propertyValue;

    public T Value
    {
        get
        {
            if (!loaded)
            {
                propertyValue = getter();
                loaded = true;
            }
            return propertyValue;
        }
    }

    public static implicit operator T(LazyProperty<T> rhs)
    {
        return rhs.Value;
    }
}

这样我就可以初始化一个这样的字段:

first = new LazyProperty<HeavyObject>(() => new HeavyObject { MyProperty = Value });

然后财产的主体可以简化为:

public HeavyObject First { get { return first; } }

这将被大多数公司使用,因为它会进入我们大多数产品共享的公共类库。

我无法决定这是不是一个好主意。我认为解决方案有一些优点,例如:

  • 少代码
  • 更漂亮的代码

缺点是,查看代码并确切地确定发生了什么会更加困难 - 特别是如果开发人员不熟悉LazyProperty类。

你怎么看?这是个好主意还是我应该抛弃它? 此外,隐式运算符是一个好主意,或者如果您应该使用此类,您是否更愿意显式使用Value属性?

欢迎提出意见和建议: - )

11 个答案:

答案 0 :(得分:7)

过分迂腐:

您建议的解决方案,以避免重复代码:

private LazyProperty<HeavyObject> first = 
  new LazyProperty<HeavyObject>(() => new HeavyObject { MyProperty = Value });
public HeavyObject First { 
  get { 
    return first; 
  } 
}

实际上是比您不想重复的代码更多的字符:

private HeavyObject first;
public HeavyObject First { 
  get {
    if (first == null) first = new HeavyObject { MyProperty = Value };
    return first;
  }
}

除此之外,我认为隐式演员使代码很难理解。我不会猜到一个简单地返回的方法,实际上最终会创建一个HeavyObject。我至少会删除隐式转换并首先返回。来自属性的值。

答案 1 :(得分:5)

根本不要这样做。

在一种情况下,通常使用这种惰性初始化属性是一种有效的设计选择:当SomeOperation();是一项昂贵的操作时(就I / O而言,就像它需要数据库命中或计算时)和当你确定你通常不需要访问它。

那就是说,默认情况下你应该进行急切的初始化,当分析器说它是你的瓶颈时,然后将其改为延迟初始化。

如果你想要创造这种抽象的冲动,那就是气味。

答案 2 :(得分:4)

当然,您至少希望LazyPropery<T>成为值类型,否则您为系统中的每个“延迟加载”属性添加了内存和GC压力。

此外,多线程场景怎么样?考虑两个线程同时请求该属性。如果没有锁定,您可能会创建底层属性的两个实例。为避免锁定常见情况,您需要进行双重检查锁定。

答案 3 :(得分:2)

我更喜欢第一个代码,因为a)它是一个具有属性的常见模式,我立即理解它,并且b)你提出的观点:没有隐藏的魔法,你必须去查找才能理解当获得价值时。

答案 4 :(得分:1)

我喜欢这个想法,因为它的代码更少,更优雅,但我会非常担心这一事实很难看到并告诉我们发生了什么。我认为它的唯一方法是使用“懒惰”方式设置变量约定,并在任何使用它的地方进行注释。现在不会有编译器或任何会强制执行这些规则的东西,所以仍然是YMMV。

最后,对我而言,这样的决定归结为谁将会关注它以及那些程序员的质量。如果你可以相信你的开发人员使用它并且评论得很好,那就去吧,但如果没有,你最好以一种易于理解和遵循的方式去做。 /我的2cents

答案 5 :(得分:1)

我不认为担心开发人员不理解是反对做这样的事情的好理由......

如果你认为那么你就不能做任何事情,因为害怕有人不理解你做了什么

您可以在中央存储库中编写教程或其他内容,我们这里有这些笔记的wiki

总的来说,我认为这是一个很好的实现想法(不想开始讨论延迟加载是否是一个好主意)

答案 6 :(得分:1)

在这种情况下我做的是创建一个Visual Studio code snippet。我认为那是你真正应该做的。

例如,当我创建ASP.NET控件时,我经常会有很多数据存储在ViewState中,所以我创建了一个这样的代码片段:

public Type Value
{
    get
    {
        if(ViewState["key"] == null)
            ViewState["key"] = someDefaultValue;
        return (Type)ViewState["key"];
    }
    set{ ViewState["key"] = value; }
}

这样,只需要一点工作就可以轻松创建代码(定义类型,键,名称和默认值)。它是可重用的,但是你没有其他开发人员可能无法理解的复杂代码的缺点。

答案 7 :(得分:0)

我喜欢你的解决方案,因为它非常聪明,但我不认为你通过使用它获得了很多。延迟在公共属性中加载私有字段绝对是可以复制代码的地方。然而,这一直是我使用的模式,而不是需要重构为公共场所的代码。

如果您进行任何序列化,您的方法将来可能成为一个问题。最初,要了解您使用自定义类型执行的操作更令人困惑。

总的来说,我赞赏你的尝试,并欣赏它的聪明,但建议你恢复原来的解决方案,原因如上所述。

答案 8 :(得分:0)

就我个人而言,我认为LazyProperty类没有提供足够的价值来证明使用它,特别是考虑到将它用于值类型的缺点(正如Kent所提到的)。如果你需要其他功能(比如使它成为多线程),它可能被称为ThreadSafeLazyProperty类。

关于隐式属性,我更喜欢“Value”属性。这是一个更多的打字,但对我来说更清楚。

答案 9 :(得分:0)

我认为这是一个有趣的想法。首先,我建议您从调用代码中隐藏Lazy Property,您不希望泄漏到您的域模型中它是懒惰的。你用隐式运算符做的就是这样。

我喜欢你如何使用这种方法来处理和抽象出锁定的细节,例如。如果你这样做,那么我认为有价值和价值。如果您确实为双锁模式添加了锁定监视,则很容易出错。

答案 10 :(得分:0)

您可以使用C#Iterators。以下文章解释了样本用法及其使用的优势。

http://hemanshubhojak.com/Home/Post?postId=3