懒惰的加载和懒惰执行模式 [英] Lazy loading and lazy execution patterns

查看:108
本文介绍了懒惰的加载和懒惰执行模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想获得一些我写的封装懒加载模式的反馈。通常,当我做延迟加载时,它看起来像这样:

I'd like to get some feedback on a class I wrote for encapsulating the lazy-load pattern. Usually, when I do lazy-loading, it looks something like this:

private SomeClass _someProperty;
public SomeClass SomeProperty
{
    get
    {
        if(this._someProperty == null)
        {
            // run code to populate/instantiate _someProperty
        }
        return this._someProperty;
    }
}

类的工作方式很基础 - 它接受通用类型参数和使用Func< T>的一个或多个方法委托。如果指定了多个委托,则使用返回非默认值的第一个代理(此特定功能对所有人都不会有用,但在我正在使用的域中是必需的)。

The way the class works is pretty basic - it accepts a generic type parameter and one or more method delegates using Func<T>. If more than one delegate is specified, the first one to return a non-default value is used (this particular feature may not be useful to everyone, but it was necessary in the domain I was using it).

这是一个简短的摘录,我相信几乎显示了这个课程:

Here is a brief excerpt that I believe shows pretty much what the class does:

/// <summary>
/// A container for an object of type <typeparamref name="T"/> whose value is not known until the <see cref="Value" /> property is accessed.
/// </summary>
/// <typeparam name="T">The type of the underlying object.</typeparam>
public class Lazy<T>
{
    /// <summary>
    /// A value representing whether the delegates have been invoked.
    /// </summary>
    private Boolean _wereValueDelegatesInvoked;

    /// <summary>
    /// Initializes a new instance of the <see cref="Lazy{T}" /> class with the specified <paramref name="valueDelegates"/>.
    /// </summary>
    /// <param name="valueDelegates">A list of delegates that can potentially return the value of the underlying object.</param>
    public Lazy(params Func<T>[] valueDelegates)
    {
        this.ValueDelegates = valueDelegates;
    }

    /// <summary>
    /// Gets the delegate that returns the value of the underlying object.
    /// </summary>
    public IEnumerable<Func<T>> ValueDelegates { get; protected set; }

    private T _value;
    /// <summary>
    /// Gets the value of the underlying object. If multiple delegates are specified, the first delegate to return a non-default value will be used.
    /// </summary>
    public T Value
    {
        get
        {
            if (!this._wereValueDelegatesInvoked)
            {
                this._value = this.GetValue();
            }
            return this._value;
        }
    }

    /// <summary>
    /// Return the value of the underlying object. If multiple delegates are specified, the first delegate to return a non-default value will be used.
    /// </summary>
    /// <returns>The value of the underlying object.</returns>
    protected T GetValue()
    {
        T value = default(T);

        if (this.ValueDelegates != null)
        {
            foreach (Func<T> valueDelegate in this.ValueDelegates)
            {
                value = valueDelegate.Invoke();
                if (!Lazy<T>.IsNullOrDefault(value)) break;
            }
        }

        this._wereValueDelegatesInvoked = true;
        return value;
    }
}

...对于那些不会唤起tl; dr的回应,这里是整个课程:

...and for those for whom this will not evoke a response of "tl;dr", here is the class in its entirety:

/// <summary>
/// A container for an object of type <typeparamref name="T"/> whose value is not known until the <see cref="Value" /> property is accessed.
/// </summary>
/// <typeparam name="T">The type of the underlying object.</typeparam>
public class Lazy<T>
{
    /// <summary>
    /// A value representing whether the delegates have been invoked.
    /// </summary>
    private Boolean _wereValueDelegatesInvoked;

    /// <summary>
    /// Initializes a new instance of the <see cref="Lazy{T}" /> class with the specified <paramref name="valueDelegate"/>.
    /// </summary>
    /// <param name="valueDelegate">A delegate that returns the value of the underlying object.</param>
    public Lazy(Func<T> valueDelegate)
    {
        this.ValueDelegates = new Func<T>[] { valueDelegate };
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Lazy{T}" /> class with the specified <paramref name="valueDelegates"/>.
    /// </summary>
    /// <param name="valueDelegates">A list of delegates that can potentially return the value of the underlying object.</param>
    public Lazy(IEnumerable<Func<T>> valueDelegates)
    {
        this.ValueDelegates = valueDelegates;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Lazy{T}" /> class with the specified <paramref name="valueDelegates"/>.
    /// </summary>
    /// <param name="valueDelegates">A list of delegates that can potentially return the value of the underlying object.</param>
    public Lazy(params Func<T>[] valueDelegates)
    {
        this.ValueDelegates = valueDelegates;
    }

    public Lazy(T value)
    {
        this._value = value;
        this._wereValueDelegatesInvoked = true;
    }

    /// <summary>
    /// Gets the delegate that returns the value of the underlying object.
    /// </summary>
    public IEnumerable<Func<T>> ValueDelegates { get; protected set; }

    private T _value;
    /// <summary>
    /// Gets the value of the underlying object. If multiple delegates are specified, the first delegate to return a non-default value will be used.
    /// </summary>
    public T Value
    {
        get
        {
            if (!this._wereValueDelegatesInvoked)
            {
                this._value = this.GetValue();
            }
            return this._value;
        }
    }

    /// <summary>
    /// Return the value of the underlying object. If multiple delegates are specified, the first delegate to return a non-default value will be used.
    /// </summary>
    /// <returns>The value of the underlying object.</returns>
    protected T GetValue()
    {
        T value = default(T);

        if (this.ValueDelegates != null)
        {
            foreach (Func<T> valueDelegate in this.ValueDelegates)
            {
                value = valueDelegate.Invoke();
                if (!Lazy<T>.IsNullOrDefault(value)) break;
            }
        }

        this._wereValueDelegatesInvoked = true;
        return value;
    }

    private static Boolean IsNullOrDefault(T value)
    {
        if (value == null) return true;
        else if (value.Equals(default(T))) return true;
        else return false;
    }

    public override Boolean Equals(Object obj)
    {
        if (this.Value == null) return (obj == null);
        return this.Value.Equals(obj);
    }

    public override Int32 GetHashCode()
    {
        if (this.Value == null) return base.GetHashCode();
        else return this.Value.GetHashCode();
    }

    /// <summary>
    /// Returns the value of the <see cref="Lazy{T}" />.
    /// </summary>
    /// <param name="value">A <see cref="Lazy{T}" /> object.</param>
    /// <returns>The value of the <see cref="Value" /> property for the <paramref name="value" /> parameter.</returns>
    public static implicit operator T(Lazy<T> value)
    {
        return value.Value;
    }

    /// <summary>
    /// Returns a <see cref="String" /> that represents the current <see cref="Lazy{T}" />.
    /// </summary>
    /// <returns>A <see cref="String" /> that represents the current <see cref="Lazy{T}" />.</returns>
    public override String ToString()
    {
        if (Lazy<T>.IsNullOrDefault(this.Value)) return null;
        else return this.Value.ToString();
    }
}

类使用如下:

    [TestMethod]
    public void ABadFakeWorkExample()
    {
        var myLazyObject = new Lazy<Object>((() => { Thread.Sleep(5000); return new Object(); }));

        Assert.IsNotNull(myLazyObject);
    }

还有一个使用多个代理的例子:

And an example using multiple delegates:

[TestMethod]
public void LazyUsesFirstNonDefaultResult()
{
    Lazy<DateTime> lazy = new Lazy<DateTime>(
        (() => { return default(DateTime); }),
        (() => { return DateTime.Now; }),
        (() => { return default(DateTime); }));

    Assert.AreNotEqual(default(DateTime), lazy.Value, "D'OH!");
}

所以这里是我想知道的:

So here's what I'd like to know:


  • 这是否超额?我的意思是很容易为我发布的第一个模式创建一个代码段,对吧?如果是这样,是不是应该使用这个类?

  • 我在ANTS中运行了一些初步测试,这似乎是相当轻量级的,但是有没有

  • 可以添加的任何内容?

  • 应该删除什么?

  • Is this overkill? I mean it's easy enough to create a snippet for the first pattern I posted, right? And if it is, is it so much so that this class shouldn't be used?
  • I ran some preliminary tests in ANTS, and this seems to be fairly lightweight, but are there any performance issues I should be worried about?
  • Anything that could be added?
  • Anything that should be taken away?

与实际问题无关的注意事项:根据从这个问题,我正在离开wiki框,没有...请注释

Note unrelated to the actual question: Based on feedback I got from this question, I'm leaving the wiki box unticked... Please comment if you really think this should be wikified.

推荐答案

测试方法有点傻,因为显然 myLazyObject引用不会为null - 您刚刚调用new。您实际上并没有测试任何转化。

The test method is a bit silly because obviously the myLazyObject reference won't be null - you've just called "new". You're not actually testing any conversions.

我不认为需要转换运算符,哈希码,等式检查或IsNullOrDefault。需要GetValue()的逻辑,但是我不会有一个Value属性一个GetValue方法。只要有财产,并把逻辑放在那里。

I don't think the conversion operator, hash code, equality checking or IsNullOrDefault are needed. The logic of GetValue() is needed, but I wouldn't have a Value property and a GetValue method. Just have the property, and put the logic in there.

我不会公开代表 - 特别是不是一个原始的数组,这意味着外界可以突变阵列。其实我不会把数组放在第一位 - 或者至少不把它存储成一个数组。我会把它存储成一个单独的代表 - 毕竟,它们是多播的。这将使您在链中的最后一个代表的返回值,而不是第一个,但我希望这不会是真正使用的问题。 (我希望实际上有多个委托指定是非常奇怪的。)

I wouldn't expose the delegates - particularly not as a raw array, which means that the outside world can mutate the array. In fact, I wouldn't take an array in the first place - or at least not store it as an array. I'd store it as a single delegate - they're multicast, after all. That will give you the return value of the last delegate in the chain rather than the first one, but I'd hope that wouldn't be a problem in real use anyway. (I hope it's pretty odd to actually have more than one delegate specified.)

我会让类密封,可能实现一个接口(例如只有 Value 属性的IProvider< T> )。对于那些只想提供价值的其他客户端,您可以在构造函数中使用T的实例 FixedProvider< T>

I would make the class sealed, possibly implementing an interface (e.g. IProvider<T>) which just has the Value property. That way for other clients who just want to provide a value, you could have FixedProvider<T> which just took an instance of T in the constructor.

想想你是否需要这样做是线程安全的。

Think about whether you need this to be thread-safe or not, too.

有很多帮助吗?

编辑:还有一件事:让代理字段只读。你不能这样做,而它是一个自动的属性,但它很容易,一旦你没有暴露它:)我也将所有的领域/自动元件在一起组合在一起,在类的顶部,所以很容易看到什么状态涉及的是。

One more thing: make the delegate field readonly. You can't do that while it's an automatic property, but it's easy once you don't expose it :) I'd also group all the fields/autoproperties together at the top of the class, so it's easy to see what the state involved is.

这篇关于懒惰的加载和懒惰执行模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆