在 C# 中编写延迟加载属性的简洁方法
Concise way to writy Lazy loaded properties in C#
当我想利用Lazy<T>
并需要参考this
时,我需要写很多样板代码:
// the private member
private Lazy<SubEventCollection> _SubEvents;
public Event()
{
// needs to be initialized in the constructor because I refer to this
_SubEvents = new Lazy<SubEventCollection3>(CreateSubEvents);
}
// the "core" body
private SubEventCollection CreateSubEvents()
{
SubEventCollection3 collection;
using ( var stream = new MemoryStream(DbSubEventsBucket) )
collection = Serializer.Deserialize<SubEventCollection3>(stream);
collection.Initialize(this);
return collection;
}
// The final property
public SubEventCollection SubEvents => _SubEvents.Value;
这一切真的有必要吗?感觉样板太多了,到处都是。没有那么多独立的样板文件,有没有什么捷径可以读起来更好看?
我可能可以将正文移到构造函数中,但我也不喜欢那样 - 即您将 很多 重要逻辑移到构造函数中。
我的首选方式类似于 Knockout.js / TypeScript。
subEvents = ko.lazyComputed(() =>
{
SubEventCollection3 sub_events;
using ( var stream = new MemoryStream(DbSubEventsBucket) )
sub_events = Serializer.Deserialize<SubEventCollection3>(stream);
sub_events.Initialize(this);
return sub_events;
})
这里没有很多 'moving parts' 而且超级简洁。
还有哪些其他选择?我注意到我经常退回到手动 'lazy' 构造。
private SubEventCollection _SubEvents;
public SubEventCollection SubEvents
{
get
{
if ( _SubEvents == null )
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
collection = Serializer.Deserialize<SubEventCollection3>(stream);
collection.Initialize(this);
_SubEvents = collection;
}
return _SubEvents;
}
}
至少这比 Lazy 方式少 'moving parts',而且我可以将所有内容放在一起(不必将一半逻辑放在构造函数中)。当然这还有很多其他缺点,比如它不是线程安全的。
我还缺少其他选择吗?
PS
我假设有两种不同的答案 - 一种用于真正的线程安全延迟加载,另一种用于简洁版本,您不关心它是否意外被调用两次。
目前我正在尝试自己的实现 - 仍然需要审查。
Class:
/// <summary>
/// Warning: might not be as performant (and safe?) as the Lazy<T>, see:
/// https://codereview.stackexchange.com/questions/207708/own-implementation-of-lazyt-object
/// </summary>
public class MyLazy<T>
{
private T _Value;
private volatile bool _Loaded;
private object _Lock = new object();
public T Get(Func<T> create)
{
if ( !_Loaded )
{
lock (_Lock)
{
if ( !_Loaded ) // double checked lock
{
_Value = create();
_Loaded = true;
}
}
}
return _Value;
}
public void Invalidate()
{
lock ( _Lock )
_Loaded = false;
}
}
使用:
MyLazy _SubEvents = new MyLazy();
public SubEventCollection SubEvents => _SubEvents.Get(LoadSubEvents);
private SubEventCollection LoadSubEvents()
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
{
var sub_event_collection = Serializer.Deserialize<SubEventCollection>(stream);
sub_event_collection.Initialize(this);
return sub_event_collection;
}
}
优点:
- 我可以将所有相关代码放在一起(不必在构造函数中放一半)
我建议将 Lazy
从 class 的内部移动到如何在方法中使用 class。在 class(包括它的 SubEventCollection
)的主体内急切地初始化 Event
,但不要在其外部使用 Event
,而是使用 Lazy<Event>
.
所以,声明:
public class Event
{
public SubEventCollection SubEvents { get; private set; }
public Event()
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
SubEvents = Serializer.Deserialize<SubEventCollection3>(stream);
SubEvents.Initialize(this);
}
}
但是,不是 return 从产生事件的任何东西中 Event
,而是 return Lazy<Event>
,为他们提供 return 根据需要提供更多数据。这还有一个好处,就是可以告知 Event
的用户获取事件数据可能是一项代价高昂的操作。
当我想利用Lazy<T>
并需要参考this
时,我需要写很多样板代码:
// the private member
private Lazy<SubEventCollection> _SubEvents;
public Event()
{
// needs to be initialized in the constructor because I refer to this
_SubEvents = new Lazy<SubEventCollection3>(CreateSubEvents);
}
// the "core" body
private SubEventCollection CreateSubEvents()
{
SubEventCollection3 collection;
using ( var stream = new MemoryStream(DbSubEventsBucket) )
collection = Serializer.Deserialize<SubEventCollection3>(stream);
collection.Initialize(this);
return collection;
}
// The final property
public SubEventCollection SubEvents => _SubEvents.Value;
这一切真的有必要吗?感觉样板太多了,到处都是。没有那么多独立的样板文件,有没有什么捷径可以读起来更好看? 我可能可以将正文移到构造函数中,但我也不喜欢那样 - 即您将 很多 重要逻辑移到构造函数中。
我的首选方式类似于 Knockout.js / TypeScript。
subEvents = ko.lazyComputed(() =>
{
SubEventCollection3 sub_events;
using ( var stream = new MemoryStream(DbSubEventsBucket) )
sub_events = Serializer.Deserialize<SubEventCollection3>(stream);
sub_events.Initialize(this);
return sub_events;
})
这里没有很多 'moving parts' 而且超级简洁。 还有哪些其他选择?我注意到我经常退回到手动 'lazy' 构造。
private SubEventCollection _SubEvents;
public SubEventCollection SubEvents
{
get
{
if ( _SubEvents == null )
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
collection = Serializer.Deserialize<SubEventCollection3>(stream);
collection.Initialize(this);
_SubEvents = collection;
}
return _SubEvents;
}
}
至少这比 Lazy 方式少 'moving parts',而且我可以将所有内容放在一起(不必将一半逻辑放在构造函数中)。当然这还有很多其他缺点,比如它不是线程安全的。
我还缺少其他选择吗?
PS 我假设有两种不同的答案 - 一种用于真正的线程安全延迟加载,另一种用于简洁版本,您不关心它是否意外被调用两次。
目前我正在尝试自己的实现 - 仍然需要审查。
Class:
/// <summary>
/// Warning: might not be as performant (and safe?) as the Lazy<T>, see:
/// https://codereview.stackexchange.com/questions/207708/own-implementation-of-lazyt-object
/// </summary>
public class MyLazy<T>
{
private T _Value;
private volatile bool _Loaded;
private object _Lock = new object();
public T Get(Func<T> create)
{
if ( !_Loaded )
{
lock (_Lock)
{
if ( !_Loaded ) // double checked lock
{
_Value = create();
_Loaded = true;
}
}
}
return _Value;
}
public void Invalidate()
{
lock ( _Lock )
_Loaded = false;
}
}
使用:
MyLazy _SubEvents = new MyLazy();
public SubEventCollection SubEvents => _SubEvents.Get(LoadSubEvents);
private SubEventCollection LoadSubEvents()
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
{
var sub_event_collection = Serializer.Deserialize<SubEventCollection>(stream);
sub_event_collection.Initialize(this);
return sub_event_collection;
}
}
优点:
- 我可以将所有相关代码放在一起(不必在构造函数中放一半)
我建议将 Lazy
从 class 的内部移动到如何在方法中使用 class。在 class(包括它的 SubEventCollection
)的主体内急切地初始化 Event
,但不要在其外部使用 Event
,而是使用 Lazy<Event>
.
所以,声明:
public class Event
{
public SubEventCollection SubEvents { get; private set; }
public Event()
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
SubEvents = Serializer.Deserialize<SubEventCollection3>(stream);
SubEvents.Initialize(this);
}
}
但是,不是 return 从产生事件的任何东西中 Event
,而是 return Lazy<Event>
,为他们提供 return 根据需要提供更多数据。这还有一个好处,就是可以告知 Event
的用户获取事件数据可能是一项代价高昂的操作。