共用方式為


抑制localsinit旗標的發出。

注意

本文是功能規格。 規格可作為功能的設計檔。 其中包含建議的規格變更,以及功能設計和開發期間所需的資訊。 這些文章會發佈,直到提議的規格變更完成並併併入目前的ECMA規格為止。

功能規格與已完成實作之間可能有一些差異。 這些差異在相關的 語言設計會議(LDM)記錄中被擷取。

您可以在關於 規格的小文章中詳閱將功能規範納入 C# 語言標準的過程

總結

允許透過 SkipLocalsInitAttribute 屬性隱藏 localsinit 旗標的發出。

動機

背景

根據 CLR 規格,不包含參考類型的區域變數不會被 VM/JIT 初始化為特定值。 從未經初始化的這類變數讀取是型別安全的,但其他情況下,行為是未定義且依賴於實現。 一般而言,未初始化的局部變數包含堆疊框架現在佔用的記憶體中留下的任何值。 這可能會導致不具決定性的行為,並難以重現 Bug。

有兩種方式可以「指派」局部變數:

  • 藉由儲存值或
  • 藉由指定 localsinit 標誌,強制從本機記憶體池分配的所有內存進行零初始化。注意:這包括局部變數和 stackalloc 資料。

不建議使用未初始化的數據,而且不允許在可驗證的程式代碼中使用。 儘管可以通過流程分析來證明,但允許驗證演算法採取保守的方法,只需確保設定 localsinit 即可。

從歷史上看,C# 編譯程式會在宣告局部變數的所有方法上發出 localsinit 旗標。

雖然 C# 採用比 CLR 規格需要更嚴格的明確指派分析(C# 也需要考慮局部變數的範圍),但並不完全保證產生的程式代碼可以正式驗證:

  • CLR 和 C# 規則可能對於將區域變數作為 out 的參數是否是 use的看法不一致。
  • 當條件已知(常量傳播)時,CLR 和 C# 規則可能在條件分支的處理上無法達成一致。
  • CLR 也可以只需要 localinits,因為這是允許的。

問題

在高效能應用程式中,強制零初始化的成本可能很明顯。 使用 stackalloc 時,特別明顯。

在某些情況下,當後續指派「終止」這類初始化時,JIT 可能會略過個別局部變數的初始零初始化。 並非所有 JIT 都這樣做,因此這類優化有限制。 無法協助 stackalloc

為了說明問題是真實的,有一個已知的錯誤,其中的方法若不包含任何 IL 本地變數,就不會有 localsinit 標記。 使用者已利用這個漏洞,方法是將 stackalloc 放入這類方法中,故意避免初始化成本。 儘管缺乏 IL 局部變數是不穩定的度量標準,但可能會因代碼生成策略的變更而有所不同。 應該修正這個 Bug,並且用戶應該獲得一種更規範且可靠的方法來抑制該旗標。

詳細設計

允許將 System.Runtime.CompilerServices.SkipLocalsInitAttribute 指定為指示編譯程式不要發出 localsinit 旗標的方式。

最終結果是,JIT可能不會將局部變數初始化為零,這在C#中大多數情況下是不可察覺的。
除此之外,stackalloc 數據不會初始化為零。 這絕對是可以觀察的,但也是最有動機的案例。

允許和辨識的屬性目標包括:MethodPropertyModuleClassStructInterfaceConstructor。 不過,編譯器不會要求屬性必須按照列出的目標來定義,也不會在意屬性是在哪個元件中定義的。

當在容器上指定 屬性時(classmodule、包含巢狀方法的方法...),旗標會影響容器中包含的所有方法。

合成方法會從邏輯容器/擁有者「繼承」旗標。

旗標只會影響實際方法主體的程式碼生成策略。 亦即 旗標對抽象方法沒有作用,而且不會傳播至覆寫/實作方法。

這明確地是一個 編譯程式功能,而不是語言功能
同樣地,編譯器命令行開關控制特定代碼生成策略的實作細節,而不需要由 C# 規格所指定。

缺點

  • 舊/其他編譯程式可能不接受 屬性。 忽略屬性是相容的行為。 可能只會導致輕微的效能下降。

  • 沒有 localinits 旗標的程式代碼可能會觸發驗證失敗。 要求這項功能的使用者通常對可驗證性不滿意。

  • 將屬性套用於層級高於單一方法層級時,會產生非本地效果,使用 stackalloc 時即可觀察到。 然而,這是最常被要求的情境。

替代方案

  • unsafe 環境中宣告方法時,省略 localinits 旗標。 在 stackalloc的情況下,這可能會導致行為從確定性隱然且危險地變更為非決定性。

  • 一律省略 localinits 旗標。 甚至比上面更糟。

  • 除非在方法主體中使用 stackalloc,否則請省略 localinits 旗標。 無法解決最受歡迎的場景,並且可能會導致代碼無法驗證,且無法恢復到可驗證狀態。

未解決的問題

  • 屬性是否應該實際新增到元數據中?

設計會議

還沒有。