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

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

问题描述

我想获得一个类我写的封装延迟加载模式的一些反馈。通常情况下,当我做延迟加载,它看起来是这样的:

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;博士,这里是整体类:

...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();
    }
}



类使用这样的:

The class is used like this:

    [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:


  • 这是矫枉过正?我的意思是它很容易创造一个片断为我张贴的第一个模式,对不对?而如果是,是否以至于该类不应该使用?

  • 我跑蚂蚁一些初步的测试,这似乎是相当轻巧,但是否有任何应采取走?
  • $ b $性能问题,我应该担心?
  • 任何可能被添加?

  • 什么b
  • 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?

注意无关的实际问题:根据反馈意见,从我的这个问题,我要离开了维基取消选中复选框...请评论,如果你真的认为这应该是wikified。

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引用不会以空 - 你刚刚叫新。你不会实际测试任何转换。

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.)

我会做密封类,可能实现一个接口(如 IProvider< T> )刚刚有属性。这样,对于其他客户谁只是想提供一个值,你可以有 FixedProvider< T> 这只是把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.

这是否很多帮助?

编辑:一件事:做委托外地只读。你不能这样做,虽然这是一个自动属性,但它很容易,一旦你不要将其暴露:)我还组中的所有字段/ autoproperties一起在类的顶部,所以很容易看到什么状态涉及的是。

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天全站免登陆