適用於 ImmutableArray 的 Roslyn 分析器和程式碼感知程式庫
.NET Compiler Platform ("Roslyn") 可協助您建置程式碼感知程式庫。 程式碼感知程式庫提供的功能,可讓您使用和工具 (Roslyn 分析器) 以最佳方式使用程式庫,或避免錯誤。 本主題說明如何使用 System.Collections.Immutable NuGet 套件來建置實際的 Roslyn 分析器,以攔截常見的錯誤。 此範例也會示範如何針對分析器找到的程式碼問題提供程式碼修正。 使用者會在 Visual Studio 燈泡圖示 UI 中看到程式碼修正,並可自動套用程式碼的修正程式。
開始使用
您需要下列項目才能建置此範例:
- Visual Studio 2015 (不是 Express Edition) 或更新版本。 您甚至可以使用免費的 Visual Studio Community Edition。
- Visual Studio SDK。 您也可以在安裝 Visual Studio 時,檢查 [通用工具] 下的 Visual Studio 擴充性工具以同時安裝 SDK。 如果您已安裝 Visual Studio,您也可以移至主功能表檔案>新>專案選擇左側瀏覽窗格中的 C#,然後選擇 [擴充性] 以安裝此 SDK。 當您選擇 [安裝 Visual Studio 擴充性工具] 階層連結專案範本時,系統會提示您下載並安裝 SDK。
- .NET Compiler Platform ("Roslyn") SDK. 您也可以移至主功能表檔案>新>專案選擇左側瀏覽窗格中的 C#,然後選擇 [擴充性] 以安裝此 SDK。 當您選擇 [下載 .NET Compiler Platform SDK] 階層連結專案範本時,系統會提示您下載並安裝 SDK。 此 SDK 包含 Roslyn 語法視覺化檢視。 這個實用的工具可協助您找出分析器中應該尋找的程式碼模型類型。 分析器基礎結構會針對特定程式碼模型類型呼叫您的程式碼,因此您的程式碼只會在必要時執行,而且只能專注於分析相關的程式碼。
問題出在哪裡?
假設您為程式庫提供 ImmutableArray (例如 System.Collections.Immutable.ImmutableArray<T>) 支援。 C# 開發人員擁有許多 .NET 陣列的經驗。 不過,由於實作中使用的 ImmutableArrays 和最佳化技術的本質,C# 開發人員直覺導致了程式庫的使用者撰寫不完整的程式碼,如下所述。 此外,在執行階段之前使用者不會看到其錯誤,這不是他們在 Visual Studio 中使用 .NET 時使用的品質體驗。
使用者熟悉撰寫程式碼,如下所示:
var a1 = new int[0];
Console.WriteLine("a1.Length = {0}", a1.Length);
var a2 = new int[] { 1, 2, 3, 4, 5 };
Console.WriteLine("a2.Length = {0}", a2.Length);
C# 開發人員熟悉建立空陣列,以填入後續的程式碼行,以及使用集合初始設定式語法。 不過,針對 ImmutableArray 在執行階段損毀相同的程式碼:
var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);
第一個錯誤是因為 ImmutableArray 實作使用結構來包裝基礎資料儲存體。 結構必須具有無參數建構函式,讓 default(T)
運算式可以傳回具有所有零或 Null 成員的結構。 當程式碼存取 b1.Length
時,因為 ImmutableArray 結構中沒有基礎記憶體陣列,所以執行階段 Null 取值錯誤。 建立空白 ImmutableArray 的正確方法是 ImmutableArray<int>.Empty
。
集合初始化設定式的錯誤會發生,因為 ImmutableArray.Add
每次呼叫新執行個體時,方法都會傳回新的執行個體。 因為 ImmutableArrays 永遠不會變更,所以當您新增元素時,您會取回新的 ImmutableArray 物件 (這可能會因為效能原因與先前現有的 ImmutableArray 共用儲存體)。 因為 b2
指向呼叫 Add()
五次前的第一個 ImmutableArray,所以b2
是預設的 ImmutableArray。 呼叫 Length 時,也會當機並出現 Null 取值錯誤。 在不手動呼叫 Add 的情況下初始化 ImmutableArray 的正確方式是使用 ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5})
。
尋找相關的語法節點類型以觸發分析器
若要開始建置分析器,請先找出您需要尋找的 SyntaxNode 類型。 從功能表檢視>其他 Windows>Roslyn 語法視覺化檢視啟動 [語法視覺化檢視]。
將編輯器的插入號放在宣告 b1
的行上。 您會看到 [語法視覺化檢視] 顯示您位於語法樹狀結構的 LocalDeclarationStatement
節點。 此節點具有 VariableDeclaration
,因此具有 VariableDeclarator
,接著具有 EqualsValueClause
,最後會有 ObjectCreationExpression
。 當您按一下節點的 [語法視覺化檢視] 樹狀結構時,編輯器視窗中的語法會醒目提示以顯示該節點所代表的程式碼。 SyntaxNode 子類型的名稱符合 C# 文法中使用的名稱。
建立分析器專案
從主功能表選擇 [檔案]> [新增]> [專案]。 在左側導覽列的 [C#] 專案底下 [新專案] 對話方塊中選擇 [擴充性],然後在右窗格中選擇 [具有程式碼修正的分析器] 專案範本。 輸入名稱並確認對話方塊。
範本會開啟 DiagnosticAnalyzer.cs 檔案。 選擇該編輯器緩衝區索引標籤。此檔案具有衍生自 DiagnosticAnalyzer
(Roslyn API 類型) 的分析器類別 (由您提供專案的名稱所組成)。 您的新類別具有 DiagnosticAnalyzerAttribute
宣告分析器與 C# 語言相關,讓編譯器探索並載入分析器。
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ImmutableArrayAnalyzer : DiagnosticAnalyzer
{}
您可以使用以 C# 程式碼為目標的 Visual Basic 來實作分析器,反之亦然。 在 DiagnosticAnalyzerAttribute 中,選擇分析器是針對一種語言還是針對兩種語言更為重要。 需要詳細模型化語言的更複雜分析器只能以單一語言為目標。 例如,若您的分析器只檢查類型名稱或公用成員名稱,則也許可以在 Visual Basic 和 C# 之間使用通用語言模型 Roslyn 供應項目。 例如,FxCop 會警告類別實作 ISerializable,但類別沒有 SerializableAttribute 屬性與語言無關,而且適用於 Visual Basic 和 C# 程式碼。
初始化分析器
在 DiagnosticAnalyzer
類別中向下捲動以查看 Initialize
方法。 編譯器會在啟動分析器時呼叫這個方法。 該方法會採用 AnalysisContext
物件,讓您的分析器取得內容資訊,並針對您想要分析的程式碼類型註冊事件的回呼。
public override void Initialize(AnalysisContext context) {}
在此方法中開啟新行並輸入「context」以查看 IntelliSense 完成清單。 您可以在完成清單中看到許多 Register...
方法來處理各種事件。 例如,第一個 RegisterCodeBlockAction
區塊會回呼您的程式碼,該程式碼通常是用大括弧括住。 註冊區塊也會回呼程式碼以取得欄位的初始值設定項目、賦予屬性的值或可選參數的值。
另一個範例是 RegisterCompilationStartAction
,在編譯開始時回呼您的程式碼,這在您需要收集許多位置的狀態時很管用。 您可以建立資料結構以收集所有使用的符號,而且每次呼叫分析器進行某些語法或符號時,您都可以儲存資料結構中每個位置的相關資訊。 當您因為編譯結束而遭到呼叫時,您可以分析儲存的所有位置,例如報告程式碼在每個 using
陳述式中使用的符號。
使用語法視覺化檢視時,您已了解當編譯器處理 ObjectCreationExpression 時您想要接收呼叫。 您可以使用此程式碼設定回呼:
context.RegisterSyntaxNodeAction(c => AnalyzeObjectCreation(c),
SyntaxKind.ObjectCreationExpression);
您註冊語法節點,並只篩選物件建立語法節點。 依照慣例,分析器作者在註冊動作時會使用 Lambda 以使分析器保持無狀態。 您可以使用 Visual Studio 功能 [使用時產生] 來建立 AnalyzeObjectCreation
方法。 這也會為您產生正確的內容參數類型。
設定分析器使用者的屬性
因此,您的分析器會適當地顯示在 Visual Studio UI 中,尋找並修改下列程式碼行,以識別您的分析器:
internal const string Category = "Naming";
將 "Naming"
變更為 "API Guidance"
。
接下來,使用 [方案總管] 尋找並開啟專案中的 Resources.resx 檔案。 您可以置入分析器、標題等的描述。您可以將所有這些的值暫時變更為 "Don't use ImmutableArray<T> constructor"
。 您可以在字串 ({0}、 {1}等) 中放置字串格式引數,並在稍後呼叫 Diagnostic.Create()
時提供要傳遞的 params
引數陣列。
分析物件建立運算式
AnalyzeObjectCreation
方法會採用程式碼分析器架構所提供的不同類型的內容。 Initialize
方法的 AnalysisContext
可讓您註冊動作回呼,以設定分析器。 例如,SyntaxNodeAnalysisContext
具有您可以四處傳遞的 CancellationToken
。 如果使用者開始在編輯器中輸入,Roslyn 將會取消執行中的分析器,以儲存工作並提升效能。 另一個範例是,此內容具有傳回物件建立語法節點的 Node 屬性。
取得節點,您可以假設此為篩選語法節點動作的類型:
var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
第一次使用您的分析器啟動 Visual Studio
建置和執行分析器來啟動 Visual Studio (按下 F5)。 因為 [方案總管] 中的 中的啟動專案是 VSIX 專案,因此執行程式碼會建置您的程式碼和 VSIX,然後啟動 VSIX 已安裝的 Visual Studio。 以這種方式啟動 Visual Studio 時,它會以不同的登錄區啟動,以便您在建置分析器時,對 Visual Studio 的主要使用不會受到測試執行個體的影響。 第一次以這種方式啟動時,Visual Studio 會在安裝 Visual Studio 之後首次啟動 Visual Studio 時執行數次類似初始化。
建立主控台專案,然後將陣列程式碼輸入主控台應用程式 Main 方法:
var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);
具有 ImmutableArray
的程式碼行有波浪線,因為您需要取得不可變的 NuGet 套件,並將 using
陳述式新增至您的程式碼。 按下 [方案總管]上 中專案節點的右指標按鈕,然後選擇 [管理 NuGet 套件]。 在 NuGet 管理員中,在搜尋方塊中輸入 “Immutable”,然後在左窗格中選擇 System.Collections.Immutable (不要選擇 Microsoft.Bcl.Immutable),然後按下右窗格中的 [安裝] 按鈕。 安裝套件會將參考新增至專案參考。
您仍然會在 ImmutableArray
下方看到紅色波浪線,因此請將插入號放在該識別碼中,然後按 Ctrl+. (句號) 以顯示建議的修正功能表,然後選擇新增適當的 using
陳述式。
暫時全部儲存並關閉 Visual Studio 的第二個執行個體,讓您處於全新狀態後繼續作業。
使用編輯完成分析器後繼續作業
在 Visual Studio 的第一個執行個體中,按下第一行插入號 F9,以在 AnalyzeObjectCreation
方法的開端設定斷點。
使用 F5 再次啟動分析器,然後在第二個 Visual Studio 執行個體中,重新開啟您上次建立的主控台應用程式。
您會在斷點返回 Visual Studio 的第一個執行個體,因為 Roslyn 編譯器看到了物件建立運算式並呼叫至您的分析器。
取得物件建立節點。 按F10 跨過設定 objectCreation
變數的行,然後在 [即時運算視窗] 中評估 "objectCreation.ToString()"
運算式。 您會看到變數指向的語法節點是程式碼 "new ImmutableArray<int>()"
,正是您在尋找的程式碼。
取得 ImmutableArray<T> Type 物件。 您需要檢查所建立的類型是否為 ImmutableArray。 首先,您會取得代表此類型的物件。 您可以使用語意模型來檢查類型,確保您擁有完全正確的類型,而且不會比較來自 ToString()
的字串。 在函式結尾輸入下列程式碼行:
var immutableArrayOfTType =
context.SemanticModel
.Compilation
.GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1");
您可以使用反引號 (`) 和泛型參數的數字,在中繼資料中指定泛型類型。 這就是為什麼您看不到 “...中繼資料名稱中的 "...ImmutableArray<T>”
語意模型有許多實用的功能,可讓您詢問符號、資料流、變數存留期等相關問題。Roslyn 會基於各種工程原因,將語法節點與語意模型分開 (效能、模型化錯誤碼等)。 您想要編譯模型來查閱參考中包含的資訊,以進行精確的比較。
您可以在編輯器視窗左側拖曳黃色執行指標。 將它拖曳到設定objectCreation
變數的行,並使用 F10 跨過新程式碼行。 如果您將滑鼠指標暫留在變數 immutableArrayOfType
上,您會看到我們在語意模型中找到確切類型。
取得物件建立運算式的類型。 本文中的「類型」會以幾種方式使用,但這表示如果您有「新的 Foo」運算式,您需要取得 Foo 的模型。 您需要取得物件建立運算式的類型,以查看它是否為 ImmutableArray<T> 類型。 再次使用語意模型,以取得物件建立運算式中類型符號 (ImmutableArray) 的符號資訊。 在函式結尾輸入下列程式碼行:
var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as INamedTypeSymbol;
因為您的分析器需要在編輯器緩衝區中處理不完整或不正確的程式碼 (例如,遺失 using
陳述式),因此您應該檢查 symbolInfo
是否為 null
。 您需要從符號資訊物件取得具名類型 (INamedTypeSymbol),才能完成分析。
比較類型。 因為有我們正在尋找的 T 開放式泛型類型,而且程式碼中的類型是具體的泛型類型,所以您可以查詢從中建構類型的符號資訊 (開放式泛型類型),並將結果與 immutableArrayOfTType
進行比較。 在方法的結尾輸入下列:
if (symbolInfo != null &&
symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{}
報告診斷。 報告診斷相當簡單。 您可以使用在專案範本中為您建立的 Rule ,該規則是在 Initialize 方法之前定義。 因為程式碼中的這種情況是一項錯誤,因此您可以變更初始化 Rule 的行,以 DiagnosticSeverity.Warning
取代 (綠色波浪線) 和 DiagnosticSeverity.Error
(紅色波浪線)。 Rule 的其餘部分會從您在逐步解說開頭附近編輯的資源進行初始化。 您也需要報告波浪線的位置,這是物件建立運算式類型規格的位置。 在 if
區塊中輸入此程式碼:
context.ReportDiagnostic(Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
您的函式看起來應該像這樣 (可能以不同方式格式化):
private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
{
var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
var immutableArrayOfTType =
context.SemanticModel
.Compilation
.GetTypeByMetadataName(
"System.Collections.Immutable.ImmutableArray`1");
var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as
INamedTypeSymbol;
if (symbolInfo != null &&
symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{
context.ReportDiagnostic(
Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
}
}
移除斷點,讓您可以看到分析器正常運作 (並停止返回 Visual Studio 的第一個執行個體)。 將執行指標拖曳至您的方法的開頭,然後按 F5 繼續執行。 當您切換回 Visual Studio 的第二個執行個體時,編譯器會再次開始檢查程式碼,並呼叫至分析器。 您可以在 ImmutableType<int>
底下看到波浪線。
為程式碼問題新增「程式碼修正」
開始之前,請先關閉 Visual Studio 的第二個執行個體,然後在第一個 Visual Studio執行個體中停止偵錯 (您正在開發分析器之處)。
新增類別。 使用 [方案總管] 中專案節點上的捷徑功能表 (右指標按鈕),然後選擇新增項目。 新增名為 BuildCodeFixProvider
的類別。 此類別必須衍生自 CodeFixProvider
,而且您必須使用 Ctrl+. (句號) 叫用新增正確 using
陳述式的程式碼修正。 此類別也需要以 ExportCodeFixProvider
屬性加上註解,而且您必須新增 using
陳述式來解析 LanguageNames
列舉。 您應該有內含下列程式碼的類別檔案:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
namespace ImmutableArrayAnalyzer
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
class BuildCodeFixProvider : CodeFixProvider
{}
清除衍生成員。 現在,將編輯器的插入號放在識別碼 CodeFixProvider
中,然後按 Ctrl+. (句號) 來清除這個抽象基底類別的實作。 這會為您產生屬性和方法。
實作 屬性。 使用下列程式碼填入 FixableDiagnosticIds
屬性的 get
本文:
return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);
Roslyn 會比對這些只是字串的識別碼來整合診斷和修正。 專案範本會為您產生診斷識別碼,而且您可以隨意變更它。 屬性中的程式碼只會從分析器類別傳回識別碼。
RegisterCodeFixAsync 方法會採用內容。 內容很重要,因為程式碼修正可以套用至多個診斷,或程式碼行上可能會有多個問題。 如果您在方法本文中輸入 "context.",IntelliSense 完成清單會顯示一些有用的成員。 有一個 CancellationToken 成員,您可以檢查是否有何項目想要取消修正。 有一個 Document 成員內含許多有用的成員,可讓您取得專案和解決方案模型物件。 當您回報診斷時,有一個 Span 成員是指定之程式碼位置的開頭和結尾。
將方法設為異步。 您需要做的第一件事是將產生的方法宣告修正為 async
方法。 程式碼修正用於擷取抽象類別的實作,即使方法傳回 Task
,也不會包含 async
關鍵字。
取得語法樹狀結構的根目錄。 若要修改程式碼,您需要使用程式碼修正所做的變更來產生新的語法樹狀結構。 您需要 Document
從內容呼叫 GetSyntaxRootAsync
。 這是異步方法,因為無法取得語法樹狀結構,可能包括從磁碟取得檔案、剖析檔案,以及為其建置 Roslyn 程式碼模型。 Visual Studio UI 應該使用 async
啟用,在這段期間回應。 在方法中,以下列取代程式碼行:
var root = await context.Document
.GetSyntaxRootAsync(context.CancellationToken);
尋找有問題的節點。 您會傳入內容範圍,但找到的節點可能不是必須變更的程式碼。 報告的診斷只提供類型識別碼的範圍 (波浪線所屬範圍),但您需要取代整個物件建立運算式,包括開頭的 new
關鍵字和結尾的括號。 將下列程式碼新增至您的方法 (並使用 Ctrl+。 新增 using
陳述式 (用於 ObjectCreationExpressionSyntax
):
var objectCreation = root.FindNode(context.Span)
.FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();
註冊燈泡 UI 的程式碼修正。 當您註冊程式碼修正時,Roslyn 會自動插入 Visual Studio 燈泡 UI。 當使用者的分析器波浪線使用不正確的 ImmutableArray<T>
建構函式時,終端使用者會發現他們可以使用 Ctrl+. (句點)。 因為您的程式碼修正提供者只會在發生問題時執行,因此您可以假設您有要尋找的物件建立運算式。 從內容參數可以將下列程式碼新增至 RegisterCodeFixAsync
方法結尾,以註冊新的程式碼修正:
context.RegisterCodeFix(
CodeAction.Create("Use ImmutableArray<T>.Empty",
c => ChangeToImmutableArrayEmpty(objectCreation,
context.Document,
c)),
context.Diagnostics[0]);
您必須將編輯器的插入號放在識別碼 CodeAction
中,然後使用 Ctrl+. (句號) 來新增此類型的適當 using
陳述式。
然後將編輯器的插入號放在 ChangeToImmutableArrayEmpty
識別碼中,然後再次使用 Ctrl+. 以產生此方法 Stub。
您新增的最後一個程式碼片段會傳遞 CodeAction
和找到的問題種類的診斷識別碼,以註冊程式碼修正。 在此範例中,此程式碼只提供一個診斷識別碼,因此您可以只傳遞診斷識別碼陣列的第一個元素。 當建立 CodeAction
時,您會傳入燈泡 UI 應該用作程式碼修正描述的文字。 您也會傳入接受 CancellationToken 並傳回新 Document 的函式。 新的 Document 有新的語法樹狀結構,其中包含呼叫 ImmutableArray.Empty
的已修補程式碼。 此程式碼片段會使用 Lambda,讓它可以關閉 objectCreation 節點和內容的 Document。
建構新語法樹狀結構。 在稍早產生 stub 的 ChangeToImmutableArrayEmpty
方法中,輸入程式碼行:ImmutableArray<int>.Empty;
。 如果您再次檢視 [語法視覺化檢視] 工具視窗,您可以看到此語法是 SimpleMemberAccessExpression 節點。 這就是此方法在新的 Document 中建構和傳回所需的方法。
ChangeToImmutableArrayEmpty
的第一個變更是在 Task<Document>
之前新增 async
,因為程式碼產生器無法假設方法應該為異步。
使用下列程式碼填入本文,讓您的方法看起來如下所示:
private async Task<Document> ChangeToImmutableArrayEmpty(
ObjectCreationExpressionSyntax objectCreation, Document document,
CancellationToken c)
{
var generator = SyntaxGenerator.GetGenerator(document);
var memberAccess =
generator.MemberAccessExpression(objectCreation.Type, "Empty");
var oldRoot = await document.GetSyntaxRootAsync(c);
var newRoot = oldRoot.ReplaceNode(objectCreation, memberAccess);
return document.WithSyntaxRoot(newRoot);
}
您會需要將編輯器的插入號放在 SyntaxGenerator
識別碼中,然後使用 Ctrl+. (句號) 來新增此類型的適當 using
陳述式。
此程式碼會使用 SyntaxGenerator
,這是建構新程式碼的實用類型。 取得具有程式碼問題的文件產生器之後,ChangeToImmutableArrayEmpty
會呼叫 MemberAccessExpression
,傳遞具有我們想要存取之成員的類型,並將成員的名稱傳遞為字串。
接下來,方法會擷取文件的根目錄,因為這可能會牽涉到一般情況下的任意工作,所以程式碼會等候此呼叫,並傳遞取消權杖。 Roslyn 程式碼模型不可變,例如使用 .NET 字串;當更新字串時,您會取得傳回的新字串物件。 當呼叫 ReplaceNode
時,您會取回新的根節點。 大部分的語法樹狀結構都是共用的 (因為不可變),但 objectCreation
節點會取代為 memberAccess
節點,以及語法樹根目錄的所有父節點。
嘗試您的程式碼修正
您現在可以按 F5 在 Visual Studio 的第二個執行個體中執行分析器。 開啟您之前使用的主控台專案。 現在您應該會看到燈泡出現在 ImmutableArray<int>
的新物件建立運算式所在的位置。 如果按 Ctrl+. (句號),您會看到程式碼修正,而且您會在燈泡 UI 中看到自動產生的程式碼差異預覽。 Roslyn 會為您建立此項。
專業提示:如果啟動 Visual Studio 的第二個執行個體,且沒有看到燈泡與程式碼修正,則可能需要清除 Visual Studio 元件快取。 清除快取會強制 Visual Studio 重新檢查元件,因此 Visual Studio 應接著挑選最新元件。 首先,關閉 Visual Studio 的第二個執行個體。 然後,到 Windows 檔案總管中瀏覽至 %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\。 (“16.0” 會使用 Visual Studio 進行版本變更。) 刪除子目錄 ComponentModelCache。
交談影片及完成程式碼專案
您可以在這裡看到所有已完成的程式碼。 子資料夾 DoNotUseImmutableArrayCollectionInitializer 和 DoNotUseImmutableArrayCtor 各有一個 C# 檔案可用來尋找問題,以及一個 C# 檔案可實作 Visual Studio 燈泡 UI 中顯示的程式碼修正。 請注意,完成的程式碼有多一點的抽象概念,會避免反覆擷取 ImmutableArray<T> 類型物件。 它會使用巢狀登錄的動作,可將類型物件儲存在每當子動作執行時即可用的內容中 (分析物件建立和分析集合初始化)。