延迟加载和懒惰的执行模式 [英] 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;博士,这里是整体类:
...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屋!