懒惰的加载和懒惰执行模式 [英] Lazy loading and lazy execution patterns
问题描述
我想获得一些我写的封装懒加载模式的反馈。通常,当我做延迟加载时,它看起来像这样:
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.)
我会让类密封,可能实现一个接口(例如只有
)。对于那些只想提供价值的其他客户端,您可以在构造函数中使用T的实例 Value
属性的IProvider< 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屋!