.Net 4.0 延迟初始化:Lazy<T>

 昨天我们说了一个.Net 4.0里面StringBuilder新增的Clear()方法及其实现,非常简单.今天要说的就稍微复杂一点了.

       语言和模式互相促进,语言让模式实现有更多可能性,越来越多优秀的被语言实现.模式实现的责任从开发者转移到语言.延迟初始化(Lazyinitialization)已经在.Net 4.0中给出了默认实现.本文将探究其使用方法和实现.

  为什么要延迟初始化(Lazy initialization)?

    平时开发能接触到延迟初始化可能是在两个地方,一个是单件模式Singletonpattern,一个是Nhibernate;这两个典型场景很能说明为什么需要延迟初始化:

1.需要初始化的对象属于昂贵的资源,直到使用的时候再初始化load-on-demand

2.初始化过程本身相当复杂,代码中要避免这种无谓复杂性,直到使用再初始化

更多延迟初始化的资料,请点击这里;

 

.Net 4.0 Lazy<T>实现Lazy initialization

        .Net 4.0中的延迟初始化的默认实现时Lazy<T>,我们通过一个简单的例子看一下怎么使用,为了方便讨论我们新建一个Student的实体类:

 

  
  
public class Student
{
public int ID
{
get ; set ;
}
public string Name
{
get ; set ;
}
}

 

延迟初始化Student:

 

  
  
Lazy < Student > student = newLazy < Student > ();
Console.WriteLine(student);
student.Value.ID
= 23 ;
student.Value.Name
= " New " ;
Console.WriteLine(student);

 

  我们在Console.WriteLine(student);  一行设置断点查看,发现IsValueCreated是false ,Value值是null,截图如下:

 

 lazy1

继续往下走,在student.Value.ID = 23;赋值的时候,再次查看student对象的值,IsValueCreated为true,Value已经不为空,见下图.

 lazy2  

我们可以猜想,应该是在Value的Get方法中对对象进行了实例化,打开Reflector v6验证:

 

Lazy
   
   
1 [Serializable, DebuggerDisplay( " ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay} " ), DebuggerTypeProxy( typeof (System_LazyDebugView <> )), ComVisible( false ), HostProtection(SecurityAction.LinkDemand, Synchronization = true , ExternalThreading = true )]
2   public class Lazy < T >
3 {
4 // Fields
5   private volatile object m_boxed;
6 [NonSerialized]
7 private readonly object m_threadSafeObj;
8 [NonSerialized]
9 private Func < T > m_valueFactory;
10 private static Func < T > PUBLICATION_ONLY_OR_ALREADY_INITIALIZED;
11
12 // Methods
13   static Lazy()
14 {
15 Lazy < T > .PUBLICATION_ONLY_OR_ALREADY_INITIALIZED = delegate {
16 return default (T);
17 };
18 }
19
20 [TargetedPatchingOptOut( " Performance critical to inline this type of method across NGen image boundaries " )]
21 public Lazy() : this (LazyThreadSafetyMode.ExecutionAndPublication)
22 {
23 }
24
25 public Lazy( bool isThreadSafe) : this (isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
26 {
27 }
28
29 [TargetedPatchingOptOut( " Performance critical to inline this type of method across NGen image boundaries " )]
30 public Lazy(Func < T > valueFactory) : this (valueFactory, LazyThreadSafetyMode.ExecutionAndPublication)
31 {
32 }
33
34 public Lazy(LazyThreadSafetyMode mode)
35 {
36 this .m_threadSafeObj = Lazy < T > .GetObjectFromMode(mode);
37 }
38
39 public Lazy(Func < T > valueFactory, bool isThreadSafe) : this (valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
40 {
41 }
42
43 public Lazy(Func < T > valueFactory, LazyThreadSafetyMode mode)
44 {
45 if (valueFactory == null )
46 {
47 throw new ArgumentNullException( " valueFactory " );
48 }
49 this .m_threadSafeObj = Lazy < T > .GetObjectFromMode(mode);
50 this .m_valueFactory = valueFactory;
51 }
52
53 private Boxed < T > CreateValue()
54 {
55 Boxed < T > boxed = null ;
56 LazyThreadSafetyMode mode = this .Mode;
57 if ( this .m_valueFactory != null )
58 {
59 try
60 {
61 if ((mode != LazyThreadSafetyMode.PublicationOnly) && ( this .m_valueFactory == Lazy < T > .PUBLICATION_ONLY_OR_ALREADY_INITIALIZED))
62 {
63 throw new InvalidOperationException(Environment.GetResourceString( " Lazy_Value_RecursiveCallsToValue " ));
64 }
65 Func < T > valueFactory = this .m_valueFactory;
66 if (mode != LazyThreadSafetyMode.PublicationOnly)
67 {
68 this .m_valueFactory = Lazy < T > .PUBLICATION_ONLY_OR_ALREADY_INITIALIZED;
69 }
70 return new Boxed < T > (valueFactory());
71 }
72 catch (Exception exception)
73 {
74 if (mode != LazyThreadSafetyMode.PublicationOnly)
75 {
76 this .m_boxed = new LazyInternalExceptionHolder < T > (exception.PrepForRemoting());
77 }
78 throw ;
79 }
80 }
81 try
82 {
83 boxed = new Boxed < T > ((T) Activator.CreateInstance( typeof (T)));
84 }
85 catch (MissingMethodException)
86 {
87 Exception ex = new MissingMemberException(Environment.GetResourceString( " Lazy_CreateValue_NoParameterlessCtorForT " ));
88 if (mode != LazyThreadSafetyMode.PublicationOnly)
89 {
90 this .m_boxed = new LazyInternalExceptionHolder < T > (ex);
91 }
92 throw ex;
93 }
94 return boxed;
95 }
96
97 private static object GetObjectFromMode(LazyThreadSafetyMode mode)
98 {
99 if (mode == LazyThreadSafetyMode.ExecutionAndPublication)
100 {
101 return new object ();
102 }
103 if (mode == LazyThreadSafetyMode.PublicationOnly)
104 {
105 return Lazy < T > .PUBLICATION_ONLY_OR_ALREADY_INITIALIZED;
106 }
107 if (mode != LazyThreadSafetyMode.None)
108 {
109 throw new ArgumentOutOfRangeException( " mode " , Environment.GetResourceString( " Lazy_ctor_ModeInvalid " ));
110 }
111 return null ;
112 }
113
114 private T LazyInitValue()
115 {
116 Boxed < T > boxed = null ;
117 switch ( this .Mode)
118 {
119 case LazyThreadSafetyMode.None:
120 boxed = this .CreateValue();
121 this .m_boxed = boxed;
122 break ;
123
124 case LazyThreadSafetyMode.PublicationOnly:
125 boxed = this .CreateValue();
126 if (Interlocked.CompareExchange( ref this .m_boxed, boxed, null ) != null )
127 {
128 boxed = (Boxed < T > ) this .m_boxed;
129 }
130 break ;
131
132 default :
133 {
134 object obj2;
135 bool lockTaken = false ;
136 try
137 {
138 Monitor.Enter(obj2 = this .m_threadSafeObj, ref lockTaken);
139 if ( this .m_boxed == null )
140 {
141 boxed = this .CreateValue();
142 this .m_boxed = boxed;
143 }
144 else
145 {
146 boxed = this .m_boxed as Boxed < T > ;
147 if (boxed == null )
148 {
149 LazyInternalExceptionHolder < T > holder = this .m_boxed as LazyInternalExceptionHolder < T > ;
150 throw holder.m_exception;
151 }
152 }
153 }
154 finally
155 {
156 if (lockTaken)
157 {
158 Monitor.Exit(obj2);
159 }
160 }
161 break ;
162 }
163 }
164 return boxed.m_value;
165 }
166
167 [OnSerializing]
168 private void OnSerializing(StreamingContext context)
169 {
170 T local1 = this .Value;
171 }
172
173 public override string ToString()
174 {
175 if ( ! this .IsValueCreated)
176 {
177 return Environment.GetResourceString( " Lazy_ToString_ValueNotCreated " );
178 }
179 return this .Value.ToString();
180 }
181
182 // Properties
183   public bool IsValueCreated
184 {
185 [TargetedPatchingOptOut( " Performance critical to inline across NGen image boundaries " )]
186 get
187 {
188 return (( this .m_boxed != null ) && ( this .m_boxed is Boxed < T > ));
189 }
190 }
191
192 internal bool IsValueFaulted
193 {
194 get
195 {
196 return ( this .m_boxed is LazyInternalExceptionHolder < T > );
197 }
198 }
199
200 internal LazyThreadSafetyMode Mode
201 {
202 get
203 {
204 if ( this .m_threadSafeObj == null )
205 {
206 return LazyThreadSafetyMode.None;
207 }
208 if ( this .m_threadSafeObj == Lazy < T > .PUBLICATION_ONLY_OR_ALREADY_INITIALIZED)
209 {
210 return LazyThreadSafetyMode.PublicationOnly;
211 }
212 return LazyThreadSafetyMode.ExecutionAndPublication;
213 }
214 }
215
216 [DebuggerBrowsable(DebuggerBrowsableState.Never)]
217 public T Value
218 {
219 get
220 {
221 Boxed < T > boxed = null ;
222 if ( this .m_boxed != null )
223 {
224 boxed = this .m_boxed as Boxed < T > ;
225 if (boxed != null )
226 {
227 return boxed.m_value;
228 }
229 LazyInternal<script type="text/javascript"> </script><script type="text/javascript"> </script><script type="text/javascript"> </script><script type="text/javascript"> </script><script type="text/javascript"> </script>ExceptionHolder < T > holder = this .m_boxed as LazyInternalExceptionHolder < T > ;
230 throw holder.m_exception;
231 }
232 Debugger.NotifyOfCrossThreadDependency();
233 return this .LazyInitValue();
234 }
235 }
236
237 internal T ValueForDebugDisplay
238 {
239 get
240 {
241 if ( ! this .IsValueCreated)
242 {
243 return default (T);
244 }
245 return ((Boxed < T > ) this .m_boxed).m_value;
246 }
247 }
248
249 // Nested Types
250   [Serializable]
251 private class Boxed
252 {
253 // Fields
254   internal T m_value;
255
256 // Methods
257   [TargetedPatchingOptOut( " Performance critical to inline this type of method across NGen image boundaries " )]
258 internal Boxed(T value)
259 {
260 this .m_value = value;
261 }
262 }
263
264 private class LazyInternalExceptionHolder
265 {
266 // Fields
267   internal Exception m_exception;
268
269 // Methods
270   [TargetedPatchingOptOut( " Performance critical to inline this type of method across NGen image boundaries " )]
271 internal LazyInternalExceptionHolder(Exception ex)
272 {
273 this .m_exception = ex;
274 }
275 }
276 }
277
278  

是不是和我们想的一样呢?

为了更清晰的看出延迟加载的处理逻辑,我按照这个代码的实现思路,去掉复杂应用场景中队异常,多线程等方面的代码,重新实现了一个Lazy<T>,就叫它YaLazy<T>吧

 

  
  
1 public class YaLazy < T >
2 {
3 private bool _isValueCreated = false ;
4 public bool IsValueCreated
5 {
6 get
7 {
8 return _isValueCreated;
9 }
10 }
11 private T _value;
12 public T Value
13 {
14 get
15 {
16 if ( this ._value != null )
17 {
18 return (T)_value;
19 }
20 return CreateValue();
21
22 }
23 }
24 private T CreateValue()
25 {
26 _isValueCreated = true ;
27 _value = (T)Activator.CreateInstance( typeof (T));
28 return _value;
29 }
30 }

使用方法和Lazy<T>类似:

  
  
1 YaLazy < Student > student2 = new YaLazy < Student > ();
2 Console.WriteLine(student2);
3 student2.Value.ID = 23 ;
4 student2.Value.Name = " New " ;
5 Console.WriteLine(student2);
6  

 

 建议单步调试~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值