LazyInitializer类和Lazy类有什么不同?我知道它们都只会在需要时初始化对象。我什么时候需要分别使用它们?
发布于 2012-08-17 22:21:18
Lazy<T> (MSDN)是一个通用包装器,它允许按需创建T的实例,方法是持有T工厂方法(Func<T>)并在访问Value属性getter时调用它。
反射-带有一组静态方法的静态类,这只是一个帮助器,它使用Activator.CreateInstance() (反射)能够实例化给定的类型实例。它不保留任何本地私有字段,也不公开任何属性,因此没有内存使用开销。
值得注意的是,这两个类都使用Func<T>作为实例工厂。
关于LazyInitializer类,MSDN只说了几句话:
这些例程避免了分配专用的延迟初始化实例,而是使用引用来确保目标在被访问时已经初始化。
PS:我发现了一个有趣的方法,LazyIntiializer如何检查实例是否已经初始化,它只是比较一个传入的引用到一个default(T),很好:
private static T EnsureInitializedCore<T>(ref T target, Func<T> valueFactory)
where T : class
{
T t = valueFactory();
if (t == null)
{
throw new InvalidOperationException(Environment.GetResourceString("Lazy_StaticInit_InvalidOperation"));
}
Interlocked.CompareExchange<T>(ref target, t, default(T));
return target;
}对我来说奇怪的是,它每次在实际检查之前都会创建一个新实例:
T t = valueFactory();
// ... and only then does check发布于 2013-04-20 13:02:14
我不确定你是否还在研究这个问题,但我最近不得不深入研究Lazy<T>和LazyInitializer.EnsureInitialized<T>()的细节,所以我想我应该分享我的发现。
首先,是一些数字。我使用这两种方法对一千万个值的批处理运行了基准测试,使用两种方法测试GC.GetTotalMemory(true)的内存使用情况,并获得实例化、第一个值访问和后续值访问的Stopwatch计时:
Lazy<T> Memory Use: 320,000,000 bytes (32B/instance)
EnsureInitialized<T>() Memory Use: N/A
Lazy<T> Instantiation Time: 622.01 ms
EnsureInitialized<T>() Inst. Time: N/A
Lazy<T> First Access: 1,373.50 ms
EnsureInitialized<T>() First Access: 72.94 ms
Lazy<T> Subsequent Accesses: 18.51 ms
EnsureInitialized<T>() Subsequent: 13.75 ms(我将LazyThreadSafetyMode.PublicationOnly与Lazy<T>'s一起使用,这看起来与LazyInitializer默认采用的线程安全方法相同。)
正如您所看到的,除非我以某种方式搞砸了我的测试(从来都不是不可能的!),在这种情况下,LazyInitializer在几乎所有可量化的方面都是优越的。它没有内存或实例化开销,并且在创建和检索值方面都更快。
那么,你为什么要使用Lazy<T>呢?首先,这些是在我的x64系统上的测试结果,在其他情况下,您可能会得到不同的结果。
Lazy<T>还可以产生更清晰、更简洁的代码。return myLazy.Value;比return LazyInitializer.EnsureInitialized(ref myValue, () => GetValue(foo));友好得多
此外,如果您要处理的是值类型,或者引用类型可以合法地为null,则Lazy<T>可以使事情变得简单得多。对于LazyInitializer,您必须使用第二个布尔值字段来跟踪该值是否已初始化,这加剧了代码清晰度问题。如果你想要更严格的线程安全,Lazy<T>也更容易使用。
总的来说,对于许多应用程序来说,大多数开销可能可以忽略不计(尽管并非总是如此--我之所以开始研究这一点,是因为我正在处理一个包含数百万个非常小的延迟加载的值的应用程序,而Lazy<T>的每个实例32字节的开销实际上开始变得不方便了)。
最后,除非您的应用程序是内存密集型的,否则我认为这通常是个人偏好的问题。对于非空引用类型,我个人认为LazyInitializer.EnsureInitialized<T>()是一种更优雅的方法,但我也可以理解代码清晰度的论点。
发布于 2016-02-03 17:32:16
正如其他答案所说,
Lazy<T>
x = new Lazy<T>(_ => new ...)进行初始化,并在访问它的任何地方使用x.Value。如果多个线程访问未初始化的对象concurrently.的Value属性,则Lazy<T>
LazyInitializer
Lazy<T>对象,直到使用时间:结论是,如果空间(可能还有时间)有限,或者不能在声明时指定所有初始化参数,则只需要使用LazyInitializer。
就我个人而言,只要有可能,我就更喜欢Lazy<T>,因为我发现它提供了更干净的代码,而且我自己也不必显式地处理初始化异常。
https://stackoverflow.com/questions/12007267
复制相似问题