LazyInitializer和Lazy<T>类之间有什么区别? 我知道他们都会根据需要初始化对象。 我什么时候需要使用它们?
答案 0 :(得分:35)
我不确定你是否还在调查这个问题,但最近我不得不仔细研究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));
此外,如果您正在处理值类型或可以合法地Lazy<T>
的引用类型,null
会使事情变得更加简单。使用LazyInitializer
,您必须使用第二个布尔字段来跟踪值是否已初始化,从而加剧了代码清晰度问题。如果您想要更严格的线程安全,Lazy<T>
也更容易使用。
在宏观方案中,大部分开销对于很多应用程序来说可能是微不足道的(尽管并非总是如此 - 我开始研究这个问题的原因是因为我正在研究涉及数百万非常小的懒惰的应用程序加载的值,Lazy<T>
的每个实例的32字节开销实际上开始变得不方便了。)
最后,除非您的应用程序占用大量内存,否则我认为这通常是个人偏好的问题。对于非null引用类型,我个人认为LazyInitializer.EnsureInitialized<T>()
是一种更优雅的方法,但我也可以挖掘代码清晰度参数。
答案 1 :(得分:18)
Lazy<T>
(MSDN)是一个通用包装器,允许通过持有T
工厂方法(T
)按需创建Func<T>
实例访问Vaue
属性getter时调用它。
LazyInitializer
- 带有一组静态方法的静态类,这只是一个使用能够实例化给定类型实例的Activator.CreateInstance()(反射)的帮助器。它不保留任何本地私有字段,也不公开任何属性,因此不会产生内存使用量开销。
值得注意的是,这两个类都使用Func<T>
作为实例工厂。
MSDN用LazyInitializer
类来表示:
这些例程避免需要分配专用的, 延迟初始化实例,而不是使用引用来确保 目标已被初始化,因为它们被访问。
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
答案 2 :(得分:4)
正如其他答案所说,
private int getImage() {
int resource = R.mipmap.screen_1;
switch(number) {
case 0:
resource = R.mipmap.screen_1;
break;
case 1:
resource = R.mipmap.screen_2;
break;
case 2:
resource = R.mipmap.screen_3;
break;
}
return resource;
}
通常会提供更清晰的代码:只需使用Lazy<T>
进行初始化,然后在访问它的任何位置使用x = new Lazy<T>(_ => new ...)
。
如果多个线程同时访问未初始化的x.Value
对象的Value
属性,则允许使用不同的预定义选项来处理初始化和异常。
Lazy<T>
节省空间,也可能节省时间:无需为您声明的每个变量初始化新的LazyInitializer
对象。
允许您延迟提供初始化参数,直到使用时间:Lazy<T>
在结论中,如果空间(可能是时间)有限,或者如果您无法在声明时指定所有初始化参数,则需要才能使用LazyInitializer.EnsureInitialized(ref x, () => new X(initParameters))
。
个人我尽可能地选择LazyInitializer
,因为我发现它提供了更清晰的代码,而且我不必自己明确处理初始化异常。
答案 3 :(得分:2)
LazyInitializer
允许您进行延迟初始化功能,而无需为每个延迟初始化对象创建类的开销。
Here是LazyInitializer
可以提供的好处。
根据您自己的要求,使用Lazy<T>
创建的开销是否对于这种情况来说太过分了。
答案 4 :(得分:1)
关于Lazy Initializing的文档很清楚地解释了它。见Lazy Initialization。简而言之,Lazy<T>
为您使用的每个T
创建一个新类(构造的泛型),并为您的每个T
实例创建该类的新实例 - 即使基础T
从未初始化。使用LazyIntializer
的 static 方法对于编码可能更复杂,但避免了Lazy<T>
包装器实例的开销。
答案 5 :(得分:0)
我认为这回答了你的问题: LazyInitialization System.Threading.ThreadLocal
的另一种方式它与Lazy相同,但唯一的区别是它以Thread Local为基础存储数据。因此,每个Thread上的值将是Initialized对象的不同副本。
来自http://www.cshandler.com/2011/09/different-ways-of-lazy-initialization.html
的更多详情答案 6 :(得分:-1)
`LazyInitializer` of an object means its object creation is deferred until it is ued first.
创建此表单的对象是为了提高性能并减少内存浪费。
鉴于要定义延迟初始化类型,我们使用Lazy<T>
类
LazyInitializer
(通用格式)
E.g:
Lazy<Orders> _orders = new Lazy<Orders>();
进一步参考: