找回密码
 立即注册
首页 业界区 业界 .Net 4.0 延迟加载:Lazy<T>

.Net 4.0 延迟加载:Lazy<T>

蒋炸役 2025-5-29 19:50:58
 昨天我们说了一个.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的实体类:
 
  1.     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:
 
  1.   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,截图如下:
 
 
1.bmp

继续往下走,在student.Value.ID = 23;赋值的时候,再次查看student对象的值,IsValueCreated为true,Value已经不为空,见下图.
 
2.bmp
  
我们可以猜想,应该是在Value的Get方法中对对象进行了实例化,打开Reflector v6验证:
 
3.gif
4.gif
Lazy
  1.   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. 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. 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  
复制代码
 
 建议单步调试~

 
5.png
6.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册