C#应该有一个懒惰的关键词
C#应该有一个惰性关键字来使延迟初始化更容易吗?
例如
public lazy string LazyInitializeString = GetStringFromDatabase();
代替
private string _backingField; public string LazyInitializeString { get { if (_backingField == null) _backingField = GetStringFromDatabase(); return _backingField; } }
我不知道关键字,但它现在有一个System.Lazy
类型。
- 它是.Net Framework 4.0的正式组成部分。
- 它允许延迟加载
member
的值。 - 它支持
lambda expression
或提供值的method
。
例:
public class ClassWithLazyMember { Lazy lazySource; public String LazyValue { get { if (lazySource == null) { lazySource = new Lazy (GetStringFromDatabase); // Same as lazySource = new Lazy (() => "Hello, Lazy World!"); // or lazySource = new Lazy (() => GetStringFromDatabase()); } return lazySource.Value; } } public String GetStringFromDatabase() { return "Hello, Lazy World!"; } }
测试:
var obj = new ClassWithLazyMember(); MessageBox.Show(obj.LazyValue); // Calls GetStringFromDatabase() MessageBox.Show(obj.LazyValue); // Does not call GetStringFromDatabase()
在上面的测试代码中, GetStringFromDatabase()
只被调用一次。 我认为这正是你想要的。
编辑:
在得到@dthorpe和@Joe的评论之后,我所能说的就是它可以是最短的:
public class ClassWithLazyMember { Lazy lazySource; public String LazyValue { get { return lazySource.Value; } } public ClassWithLazyMember() { lazySource = new Lazy (GetStringFromDatabase); } public String GetStringFromDatabase() { return "Hello, Lazy World!"; } }
因为以下不编译:
public Lazy LazyInitializeString = new Lazy (() => { return GetStringFromDatabase(); });
该属性是Lazy
类型而不是String
。 您始终需要使用LazyInitializeString.Value
访问它的值。
并且,我对如何缩短它的建议持开放态度。
您是否考虑过使用System.Lazy
?
public Lazy LazyInitializeString = new Lazy (() => { return GetStringFromDatabase(); });
(这的缺点是你需要使用LazyInitializeString.Value
而不仅仅是LazyInitializeString
。)
好的,你在评论中说Lazy
对你来说是不够的,因为它是只读的,你必须在它上面调用.Value
。
尽管如此,很明显我们想要一些东西 – 我们已经有了一个语法来描述一个要调用的动作,但是没有立即调用(实际上我们有三个; lambda,委托创建和裸方法名称作为后者 – 我们需要的最后一件事是第四件事。
但我们可以快速整理出那样做的东西。
public enum SettableLazyThreadSafetyMode // a copy of LazyThreadSafetyMode - just use that if you only care for .NET4.0 { None, PublicationOnly, ExecutionAndPublication } public class SettableLazy { private T _value; private volatile bool _isCreated; private readonly Func _factory; private readonly object _lock; private readonly SettableLazyThreadSafetyMode _mode; public SettableLazy(T value, Func factory, SettableLazyThreadSafetyMode mode) { if(null == factory) throw new ArgumentNullException("factory"); if(!Enum.IsDefined(typeof(SettableLazyThreadSafetyMode), mode)) throw new ArgumentOutOfRangeException("mode"); _lock = (_mode = mode) == SettableLazyThreadSafetyMode.None ? null : new object(); _value = value; _factory = factory; _isCreated = true; } public SettableLazy(Func factory, SettableLazyThreadSafetyMode mode) :this(default(T), factory, mode) { _isCreated = false; } public SettableLazy(T value, SettableLazyThreadSafetyMode mode) :this(value, () => Activator.CreateInstance (), mode){} public T Value { get { if(!_isCreated) switch(_mode) { case SettableLazyThreadSafetyMode.None: _value = _factory.Invoke(); _isCreated = true; break; case SettableLazyThreadSafetyMode.PublicationOnly: T value = _factory.Invoke(); if(!_isCreated) lock(_lock) if(!_isCreated) { _value = value; Thread.MemoryBarrier(); // ensure all writes involved in setting _value are flushed. _isCreated = true; } break; case SettableLazyThreadSafetyMode.ExecutionAndPublication: lock(_lock) { if(!_isCreated) { _value = _factory.Invoke(); Thread.MemoryBarrier(); _isCreated = true; } } break; } return _value; } set { if(_mode == SettableLazyThreadSafetyMode.None) { _value = value; _isCreated = true; } else lock(_lock) { _value = value; Thread.MemoryBarrier(); _isCreated = true; } } } public void Reset() { if(_mode == SettableLazyThreadSafetyMode.None) { _value = default(T); // not strictly needed, but has impact if T is, or contains, large reference type and we really want GC to collect. _isCreated = false; } else lock(_lock) //likewise, we could skip all this and just do _isCreated = false, but memory pressure could be high in some cases { _value = default(T); Thread.MemoryBarrier(); _isCreated = false; } } public override string ToString() { return Value.ToString(); } public static implicit operator T(SettableLazy lazy) { return lazy.Value; } public static implicit operator SettableLazy (T value) { return new SettableLazy (value, SettableLazyThreadSafetyMode.ExecutionAndPublication); } }
添加一些更多的构造函数重载留给读者:)
这样就足够了:
private SettableLazy _backingLazy = new SettableLazy (GetStringFromDatabase); public string LazyInitializeString { get { return _backingLazy; } set { _backingLazy = value; } }
就个人而言,我并不喜欢隐含的操作员,但他们确实表明您的要求可以得到满足。 当然不需要其他语言function。