Jon*_*ker 9 .net c# lazy-initialization
C#应该有一个惰性关键字来使延迟初始化更容易吗?
例如
public lazy string LazyInitializeString = GetStringFromDatabase();
Run Code Online (Sandbox Code Playgroud)
代替
private string _backingField;
public string LazyInitializeString
{
get
{
if (_backingField == null)
_backingField = GetStringFromDatabase();
return _backingField;
}
}
Run Code Online (Sandbox Code Playgroud)
dec*_*one 23
我不知道关键字,但它现在有一个System.Lazy<T>类型.
member.lambda expression或a method提供值.public class ClassWithLazyMember
{
Lazy<String> lazySource;
public String LazyValue
{
get
{
if (lazySource == null)
{
lazySource = new Lazy<String>(GetStringFromDatabase);
// Same as lazySource = new Lazy<String>(() => "Hello, Lazy World!");
// or lazySource = new Lazy<String>(() => GetStringFromDatabase());
}
return lazySource.Value;
}
}
public String GetStringFromDatabase()
{
return "Hello, Lazy World!";
}
}
Run Code Online (Sandbox Code Playgroud)
var obj = new ClassWithLazyMember();
MessageBox.Show(obj.LazyValue); // Calls GetStringFromDatabase()
MessageBox.Show(obj.LazyValue); // Does not call GetStringFromDatabase()
Run Code Online (Sandbox Code Playgroud)
在上面的测试代码中,GetStringFromDatabase()只调用一次.我认为这正是你想要的.
在得到@dthorpe和@Joe的评论后,我所能说的就是它可以是最短的:
public class ClassWithLazyMember
{
Lazy<String> lazySource;
public String LazyValue { get { return lazySource.Value; } }
public ClassWithLazyMember()
{
lazySource = new Lazy<String>(GetStringFromDatabase);
}
public String GetStringFromDatabase()
{
return "Hello, Lazy World!";
}
}
Run Code Online (Sandbox Code Playgroud)
因为以下不编译:
public Lazy<String> LazyInitializeString = new Lazy<String>(() =>
{
return GetStringFromDatabase();
});
Run Code Online (Sandbox Code Playgroud)
那个属性Lazy<String>不是String.你总是需要使用它来获取它的价值LazyInitializeString.Value.
并且,我对如何缩短它的建议持开放态度.
Jam*_*lis 12
你考虑过用过System.Lazy<T>吗?
public Lazy<String> LazyInitializeString = new Lazy<String>(() =>
{
return GetStringFromDatabase();
});
Run Code Online (Sandbox Code Playgroud)
(这确实有缺点,你需要使用LazyInitializeString.Value而不是仅仅LazyInitializeString.)
好吧,你在评论中说,这Lazy<T>对你来说是不够的,因为它是只读的,你必须要求.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<T>
{
private T _value;
private volatile bool _isCreated;
private readonly Func<T> _factory;
private readonly object _lock;
private readonly SettableLazyThreadSafetyMode _mode;
public SettableLazy(T value, Func<T> 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<T> factory, SettableLazyThreadSafetyMode mode)
:this(default(T), factory, mode)
{
_isCreated = false;
}
public SettableLazy(T value, SettableLazyThreadSafetyMode mode)
:this(value, () => Activator.CreateInstance<T>(), 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<T> lazy)
{
return lazy.Value;
}
public static implicit operator SettableLazy<T>(T value)
{
return new SettableLazy<T>(value, SettableLazyThreadSafetyMode.ExecutionAndPublication);
}
}
Run Code Online (Sandbox Code Playgroud)
添加一些更多的构造函数重载留给读者:)
这样就足够了:
private SettableLazy<string> _backingLazy = new SettableLazy<string>(GetStringFromDatabase);
public string LazyInitializeString
{
get
{
return _backingLazy;
}
set
{
_backingLazy = value;
}
}
Run Code Online (Sandbox Code Playgroud)
就个人而言,我并不喜欢隐含的操作员,但他们确实表明你的要求可以得到满足.当然不需要其他语言功能.