基礎才是重中之重~延遲初始化
概念:一個物件的延遲初始化(也稱延遲例項化)意味著該物件的建立將會延遲至第一次使用該物件時。 延遲初始化主要用於提高效能,避免浪費計算,並減少程式記憶體要求。
以下是最常見的方案:
-
有一個物件的建立開銷很大時,應用程式可能不會使用它。 例如,假定您在記憶體中有一個 Customer 物件,該物件的 Orders 屬性返回一個 Orders 物件。 初始化 Orders 物件可能需要建立 Orders 物件的一個大陣列(Orders[]),並可能需要資料庫連線。 如果使用者從不訪問 Orders 屬性,則沒有理由使用系統記憶體或計算週期來建立 Orders 物件。 通過使用 Lazy<Orders> 將 Orders 物件宣告為延遲初始化,可以避免在不使用該物件的情況下浪費系統資源。
-
有一個物件的建立開銷很大,您想要將建立它的時間延遲到完成其他開銷大的操作之後。 例如,假定您的應用程式在啟動時載入若干個物件例項,但只有一些物件例項需要立即執行。 通過將不必要的物件的初始化延遲到已建立必要的物件之後,可以提高應用程式的啟動效能。
儘管您可以編寫自己的程式碼來執行遲緩初始化,但我們建議改用 System..::.Lazy<(Of <(T>)>) 類。 Lazy<(Of <(T>)>) 支援執行緒安全(在一個執行緒中建立例項後,對其它線也是共享的),並提供一致的異常傳播策略。
基本語法:
1 Lazy<Orders> Orders=new Lazy<Orders>();
另外,還可以在 Lazy<(Of <(T>)>) 建構函式中傳遞一個委託,用於在建立時呼叫包裝類的特定建構函式過載,並執行所需的任何其他初始化步驟,如以下示例中所示。
1 Lazy<Orders> _orders = new Lazy<Orders>(() => new Orders(OrderSorted.CreateDate)); //可以過載指定的建構函式,如用來實現一種排序規則
在建立 Lazy 物件之後,在第一次訪問 Lazy<(Of <(T>)>) 例項的 Value 屬性之前,將不會建立 Orders 的例項。 在第一次訪問包裝型別時,將會建立並返回該包裝型別,並將其儲存起來以備任何將來的訪問。
1 // 當滿足一種條件時,再去建立orders物件 2 if (displayOrders == true) 3 { 4 DisplayOrders(_orders.Value.OrderData); 5 } 6 else 7 { 8 // Don`t waste resources getting order data. 9 }
執行緒安全初始化
預設情況下,Lazy<(Of <(T>)>) 物件是執行緒安全的。 這意味著如果建構函式未指定執行緒安全性的型別,它建立的 Lazy<(Of <(T>)>) 物件都是執行緒安全的。 在多執行緒方案中,要訪問執行緒安全的 Lazy<(Of <(T>)>) 物件的 Value 屬性的第一個執行緒將為所有執行緒上的所有後續訪問初始化該物件,並且所有執行緒都共享相同資料。 因此,由哪個執行緒初始化物件並不重要,爭用條件將是良性的。
1 // Initialize the integer to the managed thread id of the 2 // first thread that accesses the Value property. 3 Lazy<int> number = new Lazy<int>(() => 4 Thread.CurrentThread.ManagedThreadId); 5 6 Thread t1 = new Thread(() => 7 Console.WriteLine("number on t1 = {0} ThreadID = {1}", 8 number.Value, Thread.CurrentThread.ManagedThreadId)); 9 t1.Start(); 10 11 Thread t2 = new Thread(() => 12 Console.WriteLine("number on t2 = {0} ThreadID = {1}", 13 number.Value, Thread.CurrentThread.ManagedThreadId)); 14 t2.Start(); 15 16 Thread t3 = new Thread(() => 17 Console.WriteLine("number on t3 = {0} ThreadID = {1}", number.Value, 18 Thread.CurrentThread.ManagedThreadId)); 19 t3.Start(); 20 21 // Ensure that thread IDs are not recycled if the 22 // first thread completes before the last one starts. 23 t1.Join(); 24 t2.Join(); 25 t3.Join(); 26 27 /* Sample Output: 28 number on t1 = 11 ThreadID = 11 29 number on t3 = 11 ThreadID = 13 30 number on t2 = 11 ThreadID = 12 31 Press any key to exit. 32 */
實現延遲初始化屬性若要通過使用延遲初始化來實現一個公共屬性,請將該屬性的支援欄位定義為 Lazy<(Of <(T>)>) 物件,並從該屬性的 get 訪問器返回 Value 屬性。
1 class Customer 2 { 3 private Lazy<Orders> _orders; 4 public string CustomerID {get; private set;} 5 public Customer(string id) 6 { 7 CustomerID = id; 8 _orders = new Lazy<Orders>(() => 9 { 10 // You can specify any additonal 11 // initialization steps here(這個Orders不會應該Customer的例項化,而被例項化,它只會在使時,才會被例項化) 12 return new Orders(this.CustomerID); 13 }); 14 } 15 16 public Orders MyOrders 17 { 18 get 19 { 20 // Orders is created on first access here. 21 return _orders.Value; 22 } 23 } 24 }
延時例項化,在大物件時使用比較多,使用Lazy<(Of <(T>)>)我們還可以實現一種泛型的單例基類,看程式碼:
1 /// <summary> 2 /// 泛型單例基類 3 /// </summary> 4 public abstract class Singleton<TEntity> where TEntity : class 5 { 6 private static readonly Lazy<TEntity> _instance 7 = new Lazy<TEntity>(() => 8 { 9 var ctors = typeof(TEntity).GetConstructors( 10 BindingFlags.Instance 11 | BindingFlags.NonPublic 12 | BindingFlags.Public); 13 if (ctors.Count() != 1) 14 throw new InvalidOperationException(String.Format("Type {0} must have exactly one constructor.", typeof(TEntity))); 15 var ctor = ctors.SingleOrDefault(c => c.GetParameters().Count() == 0 && c.IsPrivate); 16 if (ctor == null) 17 throw new InvalidOperationException(String.Format("The constructor for {0} must be private and take no parameters.", typeof(TEntity))); 18 return (TEntity)ctor.Invoke(null); 19 }); 20 21 public static TEntity Instance 22 { 23 get { return _instance.Value; } 24 } 25 }
本文轉自部落格園張佔嶺(倉儲大叔)的部落格,原文連結:基礎才是重中之重~延遲初始化,如需轉載請自行聯絡原博主。
相關文章
- 基礎才是重中之重~delegate裡的Invoke和BeginInvoke
- 基礎才是重中之重~lock和monitor的區別
- Spring Boot 2.2中的延遲初始化Spring Boot
- DirectX11 With Windows SDK--36 延遲渲染基礎Windows
- 基於rabbitmq延遲外掛實現分散式延遲任務MQ分散式
- 從原理分析Kotlin的延遲初始化: lateinit var和by lazyKotlin
- Spring系列第十二講 lazy-init:bean延遲初始化SpringBean
- RabbitMQ延遲訊息的延遲極限是多少?MQ
- 基於Dynomite的分散式延遲佇列MIT分散式佇列
- 延遲繫結
- spring的延遲初始化bean (default-lazy-init 與 lazy-init )SpringBean
- redis 延遲佇列Redis佇列
- Mybatis延遲查詢MyBatis
- WebGL之延遲著色Web
- Laravel 延遲佇列Laravel佇列
- 疫情延遲 題解
- 實現簡單延遲佇列和分散式延遲佇列佇列分散式
- 一不小心,你就掉進了Spring延遲初始化的坑!Spring
- 延遲阻塞佇列 DelayQueue佇列
- 延遲繫結與retdlresolve
- 從庫延遲案例分析
- hyperf redis延遲佇列Redis佇列
- 基於訊息佇列(RabbitMQ)實現延遲任務佇列MQ
- 美國伺服器延遲高怎麼辦,如何解決延遲問題伺服器
- java基礎-初始化與清理-成員初始化Java
- mysql主從延遲複製MySql
- 延遲靜態繫結——static
- 前向渲染和延遲渲染
- Flink 鏈路延遲測量
- 主從延遲調優思路
- PostgreSQL中的複製延遲SQL
- 3.6 延遲例項終止
- RabbitMQ實戰《延遲佇列》MQ佇列
- Mybatis延遲載入、快取MyBatis快取
- MySQL 延遲從庫介紹MySql
- [Redis]延遲訊息佇列Redis佇列
- osg使用整理(11):延遲渲染
- RabbitMQ 實現延遲佇列MQ佇列
- laravel 實戰延遲解鎖Laravel