關于靜態類中的靜態變量賦值:
public static class ActorEventDefine{public static readonly int ScoreChange = RuntimeId.ToRuntimeId("ActorEventDefine.ScoreChange");public static readonly int GameOver = RuntimeId.ToRuntimeId("ActorEventDefine.GameOver");public static readonly int EnemyDead = RuntimeId.ToRuntimeId("ActorEventDefine.EnemyDead");public static readonly int PlayerDead = RuntimeId.ToRuntimeId("ActorEventDefine.PlayerDead");public static readonly int AsteroidExplosion = RuntimeId.ToRuntimeId("ActorEventDefine.AsteroidExplosion");public static readonly int EnemyFireBullet = RuntimeId.ToRuntimeId("ActorEventDefine.EnemyFireBullet");public static readonly int PlayerFireBullet = RuntimeId.ToRuntimeId("ActorEventDefine.PlayerFireBullet");}
在 Unity 中,static readonly
字段的賦值時機遵循 .NET 的規則:
當第一次訪問 ActorEventDefine
類本身或其中的任何成員時,CLR 會執行該類的類型初始化(Type Initializer)。
此時,所有 static readonly
字段會按順序執行賦值(即調用 RuntimeId.ToRuntimeId(...)
)。
關鍵點:
-
延遲初始化:直到首次訪問時才會執行,而非游戲啟動時立即執行。
-
線程安全:CLR 保證靜態構造函數是線程安全的,多線程訪問時不會重復初始化。
-
性能:由于
RuntimeId.ToRuntimeId
可能是計算密集型操作,首次訪問可能會有輕微延遲,但后續訪問無額外開銷。
具體流程:
-
首次訪問觸發:
當你第一次通過代碼訪問ActorEventDefine.ScoreChange
或其他字段,或顯式使用ActorEventDefine
類時(例如在初始化時注冊事件監聽),CLR 會檢查該類是否已初始化。 -
靜態構造函數執行:
由于代碼中沒有顯式定義static ActorEventDefine()
構造函數,編譯器會自動生成一個隱式的靜態構造函數,其中包含所有static readonly
字段的賦值邏輯。 -
字段初始化順序:
所有static readonly
字段會按代碼中的順序初始化(ScoreChange
→GameOver
→ ...),且僅執行一次,后續訪問直接復用已初始化的值
出處:
根據 Microsoft C# 編程指南官方文檔 的說明:
“如果未提供靜態構造函數來初始化靜態字段,會將所有靜態字段初始化為其默認值。如果靜態構造函數類中存在靜態字段變量初始值設定項,它們將以在類聲明中顯示的文本順序運行。初始值設定項緊接著靜態構造函數之前運行。”
這意味著,當你沒有顯式定義一個 static ActorEventDefine()
構造函數時,編譯器會自動生成一個隱式的靜態構造函數(即“類型構造器”),并將所有 static readonly
字段的初始化代碼插入到這個構造器中,按聲明順序執行。
此外,CLR via C# 一書中也明確指出:
“當你使用內聯語法初始化靜態字段時,C# 編譯器會將這些初始化語句移動到它自動生成的類型構造器中。”
因此,“編譯器會自動生成一個隱式的靜態構造函數”這一行為在官方文檔和技術規范中均有明確依據,并非推測。