昨天我们说了一个.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,截图如下:
继续往下走,在student.Value.ID = 23;赋值的时候,再次查看student对象的值,IsValueCreated为true,Value已经不为空,见下图.
我们可以猜想,应该是在Value的Get方法中对对象进行了实例化,打开Reflector v6验证:
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 LazyInternalExceptionHolder<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
建议单步调试~