昨天我们说了一个.Net 4.0里面StringBuilder新增的Clear()方法及其实现,非常简单.今天要说的就稍微复杂一点了.
语言和模式互相促进,语言让模式实现有更多可能性,越来越多优秀的被语言实现.模式实现的责任从开发者转移到语言.延迟初始化(Lazyinitialization)已经在.Net 4.0中给出了默认实现.本文将探究其使用方法和实现.
为什么要延迟初始化(Lazy initialization)?
平时开发能接触到延迟初始化可能是在两个地方,一个是单件模式Singletonpattern,一个是Nhibernate;这两个典型场景很能说明为什么需要延迟初始化:
1.需要初始化的对象属于昂贵的资源,直到使用的时候再初始化load-on-demand
2.初始化过程本身相当复杂,代码中要避免这种无谓复杂性,直到使用再初始化
更多延迟初始化的资料,请点击这里;
.Net 4.0 Lazy实现Lazy initialization
.Net 4.0中的延迟初始化的默认实现时Lazy,我们通过一个简单的例子看一下怎么使用,为了方便讨论我们新建一个Student的实体类:
- public class Student<br> {<br> public int ID<br> {<br> get;set;<br> }<br> public string Name<br> {<br> get; set;<br> }<br> }<br>
复制代码
延迟初始化Student:
- Lazy<Student> student = newLazy<Student>();<br> Console.WriteLine(student);<br> student.Value.ID = 23;<br> student.Value.Name = "New";<br> Console.WriteLine(student);<br>
复制代码
我们在Console.WriteLine(student); 一行设置断点查看,发现IsValueCreated是false ,Value值是null,截图如下:
继续往下走,在student.Value.ID = 23;赋值的时候,再次查看student对象的值,IsValueCreated为true,Value已经不为空,见下图.
我们可以猜想,应该是在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)]<br> 2 public class Lazy<T><br> 3 {<br> 4 // Fields<br> 5 private volatile object m_boxed;<br> 6 [NonSerialized]<br> 7 private readonly object m_threadSafeObj;<br> 8 [NonSerialized]<br> 9 private Func<T> m_valueFactory;<br> 10 private static Func<T> PUBLICATION_ONLY_OR_ALREADY_INITIALIZED;<br> 11 <br> 12 // Methods<br> 13 static Lazy()<br> 14 {<br> 15 Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED = delegate {<br> 16 return default(T);<br> 17 };<br> 18 }<br> 19 <br> 20 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]<br> 21 public Lazy() : this(LazyThreadSafetyMode.ExecutionAndPublication)<br> 22 {<br> 23 }<br> 24 <br> 25 public Lazy(bool isThreadSafe) : this(isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)<br> 26 {<br> 27 }<br> 28 <br> 29 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]<br> 30 public Lazy(Func<T> valueFactory) : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication)<br> 31 {<br> 32 }<br> 33 <br> 34 public Lazy(LazyThreadSafetyMode mode)<br> 35 {<br> 36 this.m_threadSafeObj = Lazy<T>.GetObjectFromMode(mode);<br> 37 }<br> 38 <br> 39 public Lazy(Func<T> valueFactory, bool isThreadSafe) : this(valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)<br> 40 {<br> 41 }<br> 42 <br> 43 public Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode)<br> 44 {<br> 45 if (valueFactory == null)<br> 46 {<br> 47 throw new ArgumentNullException("valueFactory");<br> 48 }<br> 49 this.m_threadSafeObj = Lazy<T>.GetObjectFromMode(mode);<br> 50 this.m_valueFactory = valueFactory;<br> 51 }<br> 52 <br> 53 private Boxed<T> CreateValue()<br> 54 {<br> 55 Boxed<T> boxed = null;<br> 56 LazyThreadSafetyMode mode = this.Mode;<br> 57 if (this.m_valueFactory != null)<br> 58 {<br> 59 try<br> 60 {<br> 61 if ((mode != LazyThreadSafetyMode.PublicationOnly) && (this.m_valueFactory == Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED))<br> 62 {<br> 63 throw new InvalidOperationException(Environment.GetResourceString("Lazy_Value_RecursiveCallsToValue"));<br> 64 }<br> 65 Func<T> valueFactory = this.m_valueFactory;<br> 66 if (mode != LazyThreadSafetyMode.PublicationOnly)<br> 67 {<br> 68 this.m_valueFactory = Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED;<br> 69 }<br> 70 return new Boxed<T>(valueFactory());<br> 71 }<br> 72 catch (Exception exception)<br> 73 {<br> 74 if (mode != LazyThreadSafetyMode.PublicationOnly)<br> 75 {<br> 76 this.m_boxed = new LazyInternalExceptionHolder<T>(exception.PrepForRemoting());<br> 77 }<br> 78 throw;<br> 79 }<br> 80 }<br> 81 try<br> 82 {<br> 83 boxed = new Boxed<T>((T) Activator.CreateInstance(typeof(T)));<br> 84 }<br> 85 catch (MissingMethodException)<br> 86 {<br> 87 Exception ex = new MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT"));<br> 88 if (mode != LazyThreadSafetyMode.PublicationOnly)<br> 89 {<br> 90 this.m_boxed = new LazyInternalExceptionHolder<T>(ex);<br> 91 }<br> 92 throw ex;<br> 93 }<br> 94 return boxed;<br> 95 }<br> 96 <br> 97 private static object GetObjectFromMode(LazyThreadSafetyMode mode)<br> 98 {<br> 99 if (mode == LazyThreadSafetyMode.ExecutionAndPublication)<br>100 {<br>101 return new object();<br>102 }<br>103 if (mode == LazyThreadSafetyMode.PublicationOnly)<br>104 {<br>105 return Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED;<br>106 }<br>107 if (mode != LazyThreadSafetyMode.None)<br>108 {<br>109 throw new ArgumentOutOfRangeException("mode", Environment.GetResourceString("Lazy_ctor_ModeInvalid"));<br>110 }<br>111 return null;<br>112 }<br>113 <br>114 private T LazyInitValue()<br>115 {<br>116 Boxed<T> boxed = null;<br>117 switch (this.Mode)<br>118 {<br>119 case LazyThreadSafetyMode.None:<br>120 boxed = this.CreateValue();<br>121 this.m_boxed = boxed;<br>122 break;<br>123 <br>124 case LazyThreadSafetyMode.PublicationOnly:<br>125 boxed = this.CreateValue();<br>126 if (Interlocked.CompareExchange(ref this.m_boxed, boxed, null) != null)<br>127 {<br>128 boxed = (Boxed<T>) this.m_boxed;<br>129 }<br>130 break;<br>131 <br>132 default:<br>133 {<br>134 object obj2;<br>135 bool lockTaken = false;<br>136 try<br>137 {<br>138 Monitor.Enter(obj2 = this.m_threadSafeObj, ref lockTaken);<br>139 if (this.m_boxed == null)<br>140 {<br>141 boxed = this.CreateValue();<br>142 this.m_boxed = boxed;<br>143 }<br>144 else<br>145 {<br>146 boxed = this.m_boxed as Boxed<T>;<br>147 if (boxed == null)<br>148 {<br>149 LazyInternalExceptionHolder<T> holder = this.m_boxed as LazyInternalExceptionHolder<T>;<br>150 throw holder.m_exception;<br>151 }<br>152 }<br>153 }<br>154 finally<br>155 {<br>156 if (lockTaken)<br>157 {<br>158 Monitor.Exit(obj2);<br>159 }<br>160 }<br>161 break;<br>162 }<br>163 }<br>164 return boxed.m_value;<br>165 }<br>166 <br>167 [OnSerializing]<br>168 private void OnSerializing(StreamingContext context)<br>169 {<br>170 T local1 = this.Value;<br>171 }<br>172 <br>173 public override string ToString()<br>174 {<br>175 if (!this.IsValueCreated)<br>176 {<br>177 return Environment.GetResourceString("Lazy_ToString_ValueNotCreated");<br>178 }<br>179 return this.Value.ToString();<br>180 }<br>181 <br>182 // Properties<br>183 public bool IsValueCreated<br>184 {<br>185 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]<br>186 get<br>187 {<br>188 return ((this.m_boxed != null) && (this.m_boxed is Boxed<T>));<br>189 }<br>190 }<br>191 <br>192 internal bool IsValueFaulted<br>193 {<br>194 get<br>195 {<br>196 return (this.m_boxed is LazyInternalExceptionHolder<T>);<br>197 }<br>198 }<br>199 <br>200 internal LazyThreadSafetyMode Mode<br>201 {<br>202 get<br>203 {<br>204 if (this.m_threadSafeObj == null)<br>205 {<br>206 return LazyThreadSafetyMode.None;<br>207 }<br>208 if (this.m_threadSafeObj == Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED)<br>209 {<br>210 return LazyThreadSafetyMode.PublicationOnly;<br>211 }<br>212 return LazyThreadSafetyMode.ExecutionAndPublication;<br>213 }<br>214 }<br>215 <br>216 [DebuggerBrowsable(DebuggerBrowsableState.Never)]<br>217 public T Value<br>218 {<br>219 get<br>220 {<br>221 Boxed<T> boxed = null;<br>222 if (this.m_boxed != null)<br>223 {<br>224 boxed = this.m_boxed as Boxed<T>;<br>225 if (boxed != null)<br>226 {<br>227 return boxed.m_value;<br>228 }<br>229 LazyInternalExceptionHolder<T> holder = this.m_boxed as LazyInternalExceptionHolder<T>;<br>230 throw holder.m_exception;<br>231 }<br>232 Debugger.NotifyOfCrossThreadDependency();<br>233 return this.LazyInitValue();<br>234 }<br>235 }<br>236 <br>237 internal T ValueForDebugDisplay<br>238 {<br>239 get<br>240 {<br>241 if (!this.IsValueCreated)<br>242 {<br>243 return default(T);<br>244 }<br>245 return ((Boxed<T>) this.m_boxed).m_value;<br>246 }<br>247 }<br>248 <br>249 // Nested Types<br>250 [Serializable]<br>251 private class Boxed<br>252 {<br>253 // Fields<br>254 internal T m_value;<br>255 <br>256 // Methods<br>257 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]<br>258 internal Boxed(T value)<br>259 {<br>260 this.m_value = value;<br>261 }<br>262 }<br>263 <br>264 private class LazyInternalExceptionHolder<br>265 {<br>266 // Fields<br>267 internal Exception m_exception;<br>268 <br>269 // Methods<br>270 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]<br>271 internal LazyInternalExceptionHolder(Exception ex)<br>272 {<br>273 this.m_exception = ex;<br>274 }<br>275 }<br>276 }<br>277 <br>278
复制代码 是不是和我们想的一样呢?
为了更清晰的看出延迟加载的处理逻辑,我按照这个代码的实现思路,去掉复杂应用场景中队异常,多线程等方面的代码,重新实现了一个Lazy,就叫它YaLazy吧
- 1 public class YaLazy<T><br> 2 {<br> 3 private bool _isValueCreated = false;<br> 4 public bool IsValueCreated<br> 5 {<br> 6 get<br> 7 {<br> 8 return _isValueCreated;<br> 9 }<br>10 }<br>11 private T _value;<br>12 public T Value<br>13 {<br>14 get<br>15 {<br>16 if (this._value != null)<br>17 {<br>18 return (T)_value;<br>19 }<br>20 return CreateValue();<br>21 <br>22 }<br>23 }<br>24 private T CreateValue()<br>25 {<br>26 _isValueCreated = true;<br>27 _value = (T)Activator.CreateInstance(typeof(T));<br>28 return _value;<br>29 }<br>30 }
复制代码 使用方法和Lazy类似:- 1 YaLazy<Student> student2 = new YaLazy<Student>();<br>2 Console.WriteLine(student2);<br>3 student2.Value.ID = 23;<br>4 student2.Value.Name = "New";<br>5 Console.WriteLine(student2);<br>6
复制代码
建议单步调试~
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |