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。