首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

.NET Framework 4.0 Lazy<T> 實現延遲加載:原理、用法與最佳實踐場景

     編輯:ASP.NET 基礎

.NET 4.0 引入 System.Lazy<T> 類,提供線程安全的延遲初始化封裝,避免不必要的對象創建開銷,提升啟動性能與內存利用率。本文詳解其構造方式、核心屬性、實戰示例及適用場景。

核心概念:即用即創建

Lazy<T> 封裝一個值工廠委托,僅在首次訪問 Value 屬性時執行初始化,後續訪問直接返回緩存結果。支持多線程安全初始化(默認模式)。

基本用法:構造與訪問

// 構造:傳入無參、返回 T 的委托(Lambda/靜態方法/實例方法均可)
Lazy<string> lazyString = new Lazy<string>(TestLazy.GetString);

// 判斷是否已初始化
Console.WriteLine(lazyString.IsValueCreated); // False

// 首次訪問 Value 觸發初始化
Console.WriteLine(lazyString.Value); // 輸出當前時間字符串

// 再次訪問直接返回緩存值
Console.WriteLine(lazyString.IsValueCreated); // True

靜態工廠方法示例:

public class TestLazy
{
    public static string GetString()
    {
        return DateTime.Now.ToLongTimeString();
    }
}

進階示例:導航屬性延遲加載(模擬 ORM 行為)

博客用戶包含多篇文章,加載用戶時不預加載文章,僅在訪問 Articles 時查詢:

public class BlogUser
{
    public int Id { get; private set; }
    // Lazy 包裝 List<Article>,工廠委托捕獲外部 id
    public Lazy<List<Article>> Articles { get; private set; }

    public BlogUser(int id)
    {
        this.Id = id;
        Articles = new Lazy<List<Article>>(() => ArticleServices.GetArticlesByUserId(id));
        Console.WriteLine("BlogUser Initializer");
    }
}

public class Article
{
    public int Id { get; set; }
    public string Title { get; set; }
    public DateTime PublishDate { get; set; }
}

public static class ArticleServices
{
    public static List<Article> GetArticlesByUserId(int userId)
    {
        Console.WriteLine("Article Initializer - 此時才查詢數據庫");
        return new List<Article> {
            new Article { Id = 1, Title = "Lazy Load", PublishDate = DateTime.Parse("2011-4-20") },
            new Article { Id = 2, Title = "Delegate", PublishDate = DateTime.Parse("2011-4-21") },
            // ...
        };
    }
}

// 使用
var blogUser = new BlogUser(1);
Console.WriteLine("blogUser has been initialized"); // 未觸發 Articles 初始化
foreach (var article in blogUser.Articles.Value) // 此處觸發查詢
{
    Console.WriteLine(article.Title);
}

運行結果時序

BlogUser Initializer
blogUser has been initialized
Article Initializer - 此時才查詢數據庫
Lazy Load
Delegate
...

核心屬性與線程安全模式

屬性/構造參數說明
Value獲取實例,首次訪問觸發初始化,後續返回緩存。
IsValueCreated只讀,指示是否已完成初始化。
LazyThreadSafetyMode構造函數可指定:
ExecutionAndPublication(默認,全鎖雙檢查,線程安全);
PublicationOnly(允許並發初始化,取首個完成結果);
None(不保證線程安全,性能最好)。

適用場景總結

  1. 昂貴子對象按需創建:父對象構造輕量,子對象初始化耗時(大數據查詢、複雜計算、資源連接),且業務流程可能根本不訪問該子對象。
  2. 應用啟動加速:啟動階段需創建多個服務/管理器,但僅少數立即使用。將非關鍵服務包裝為 Lazy<T>,主線程快速啟動,背景按需初始化。
  3. 單例模式線程安全實現:private static readonly Lazy<MyService> _instance = new Lazy<MyService>(() => new MyService()); 替代雙重檢查鎖。
  4. ORM 導航屬性延遲加載:Entity Framework、NHibernate、Hibernate (Java) 等廣泛應用此模式。

注意事項

  • 異常傳播:工廠委托拋出的異常會在首次訪問 Value 時拋出,後續訪問不會重試(除非使用 PublicationOnly 模式)。
  • 不可重置:一次初始化後無法重新初始化,若需重置請新建 Lazy<T> 實例。
  • 避免循環依賴:A 的 Lazy 工廠引用 B,B 的 Lazy 工廠引用 A,會導致死鎖或棧溢出。

Lazy<T> 是 .NET 基礎類庫中極具實用價值的類型,善用它能讓資源分配更精準、啟動更迅捷、代碼意圖更清晰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved