C# 9.0新特性詳解系列之三:模塊初始化器


1 背景動機

關於模塊或者程序集初始化工作一直是C#的一個痛點,微軟內部外部都有大量的報告反應很多客戶一直被這個問題困擾,這還不算沒有統計上的客戶。那么解決這個問題,還有基於什么樣的考慮呢?

  • 在庫加載的時候,能以最小的開銷、無需用戶顯式調用任何接口,使客戶做一些期望的和一次性的初始化。

  • 當前靜態構造函數方法的一個最大的問題是運行時會對帶有靜態構造函數的類型做一些額外的檢查。這是因為要決定靜態構造函數是否需要被運行所必須的一步,但是這個又有着顯著的開銷影響。

  • 使源代碼生成器在不需要用戶顯式調用一些東西的情況下能運行一些全局的初始化邏輯。

2 詳細設計

C# 9.0將模塊初始化器設計為一個Attribute,用這個Attribute來修飾進行模塊初始化邏輯的方法,就實現了模塊初始化功能。這個Attribute被命名為ModuleInitializerAttribute,具體定義如下:

using System;
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class ModuleInitializerAttribute : Attribute { }
}

如果要使用模塊初始化器,你只要將ModuleInitializerAttribute用在符合下面要求的方法上就可以了。

  1. 該方法必須使靜態的、無參的、返回值為void的函數。

  2. 該方法不能是泛型或者包含在泛型類型里

  3. 該方法必須是可從其所在模塊里訪問的。也就是說,方法的有效訪問符必須是internal或者public,不能是局部方法。

using System.Runtime.CompilerServices;
class MyClass
{
    [ModuleInitializer]
    internal static void Initializer()
    {
        // ...
    }
}

被修飾為ModuleInitializerAttribute的靜態方法會被編譯器在編譯時,在全局的靜態構造函數中生成此代碼調用。如果有多個被修飾為初始化器的函數,則每個函數生成一個初始化器代碼調用,這些初始化器代碼調用代碼會按照一定的順序(類型名稱順序和代碼順序)生成。當模塊在被加載時,全局靜態構造函數開始執行,從而完成模塊代碼初始化工作。

3 問題與最佳實踐

模塊初始化器與靜態構造函數之間有着一定的關聯影響。因為模塊初始化器是一個靜態方法,因而其被調用執行前,必然會引起其所處類型的靜態構造函數的執行。請參考下列示例:

static class ModuleInit
{
    static ModuleInit()
    {
        //先執行
        Console.WriteLine("ModuleInit靜態構造函數 cctor");
    }

    [ModuleInitializer]
    internal static void Initializer()
    {
        //在靜態構造函數執行后才執行
        Console.WriteLine("模塊初始化器");
    }
}

在一個模塊中指定多個模塊初始化器的時候,他們之間的順序也是一個值得注意的問題。以上這些問題的存在,就要求我們注意以下幾點:

  • 在指定了模塊初始化器的類型中,不要在靜態構造函數中,寫與模塊初始化器中代碼有着順序依賴代碼,最好的就是不要使用靜態構造函數。

  • 多個模塊初始化器之間的代碼,也不要有任何依賴關系,保持各個初始化器代碼的獨立性。

4 結束語

日常開發中,我們通常需要在模塊初始化的時候,做一些前置性的准備工作,以前常采用靜態構造函數這種不具有全局性方法,局限性很大,現在,這些都得到了完美解決。

如對您有價值,請推薦,您的鼓勵是我繼續的動力,在此萬分感謝。關注本人公眾號“碼客風雲”,享第一時間閱讀最新文章。

碼客風雲

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM