ImmutableArrays에 대한 Roslyn 분석기 및 코드 인식 라이브러리
.NET Compiler Platform(“Roslyn”)은 코드 인식 라이브러리를 빌드하는 데 도움이 됩니다. 코드 인식 라이브러리는 라이브러리를 가장 좋은 방법으로 사용하거나 오류를 방지하는 데 도움이 되는 도구(Roslyn 분석기)를 사용할 수 있는 기능을 제공합니다. 이 항목에서는 System.Collections.Immutable NuGet 패키지를 사용할 때 일반적인 오류를 catch하기 위해 실제 Roslyn 분석기를 빌드하는 방법을 보여 줍니다. 이 예제에서는 분석기에서 찾은 코드 문제에 대한 코드 수정을 제공하는 방법도 보여 줍니다. 사용자는 Visual Studio 전구 UI에서 코드 수정을 보고 코드에 대한 수정 사항을 자동으로 적용할 수 있습니다.
시작하기
이 예제를 빌드하려면 다음이 필요합니다.
- Visual Studio 2015(Express Edition 아님) 이상 버전. 무료 Visual Studio Community 버전부터 시작할 수 있습니다.
- 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 Syntax Visualizer가 포함됩니다. 이 유용한 도구는 분석기에서 찾아야 하는 코드 모델 유형을 파악하는 데 도움이 됩니다. 분석기 인프라는 특정 코드 모델 형식에 대한 코드를 호출하므로 코드는 필요한 경우에만 실행되며 관련 코드 분석에만 집중할 수 있습니다.
어떤 문제 때문인가요?
ImmutableArray(예: System.Collections.Immutable.ImmutableArray<T>)를 지원하는 라이브러리를 제공한다고 상상해 보세요. C# 개발자는 .NET 배열에 대한 경험이 많습니다. 그러나 구현에 사용되는 ImmutableArrays 및 최적화 기술의 특성으로 인해 라이브러리 사용자는 C# 개발자의 직관에 따라 아래 설명된 대로 손상된 코드를 작성하게 됩니다. 또한 사용자는 런타임까지 오류를 볼 수 없습니다. 이는 .NET을 사용하여 Visual Studio에서 사용되는 품질 환경이 아닙니다.
사용자는 다음과 같은 코드를 작성하는 데 익숙합니다.
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)
식이 모두 0 또는 null 멤버가 있는 구조체를 반환할 수 있도록 매개 변수가 없는 생성자가 있어야 합니다. 코드가 b1.Length
에 액세스할 때 ImmutableArray 구조체에 기본 스토리지 배열이 없기 때문에 런타임 null 역참조 오류가 발생합니다. 빈 ImmutableArray를 만드는 올바른 방법은 ImmutableArray<int>.Empty
입니다.
컬렉션 이니셜라이저의 오류는 ImmutableArray.Add
메서드를 호출할 때마다 새 인스턴스를 반환하기 때문에 발생합니다. ImmutableArrays는 변경되지 않으므로 새 요소를 추가할 때 새 ImmutableArray 개체를 다시 가져옵니다(이전의 ImmutableArray와 성능상의 이유로 스토리지를 공유할 수 있음). b2
는 Add()
를 다섯 번 호출하기 전에 첫 번째 ImmutableArray를 가리키므로 b2
는 기본 ImmutableArray입니다. 또한 길이를 호출하면 null 역참조 오류도 발생합니다. Add를 수동으로 호출하지 않고 ImmutableArray를 초기화하는 올바른 방법은 ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5})
를 사용하는 것입니다.
분석기를 트리거하는 관련 구문 노드 형식 찾기
분석기 빌드를 시작하려면 먼저 찾아야 하는 SyntaxNode 형식을 파악합니다. 보기>기타 Windows>Roslyn Syntax Visualizer 메뉴에서 Syntax Visualizer를 시작합니다.
b1
을 선언하는 줄에 편집기의 캐럿을 배치합니다. Syntax Visualizer가 구문 트리의 LocalDeclarationStatement
노드에 있음을 보여줍니다. 이 노드에는 VariableDeclaration
이 있고 차례로 VariableDeclarator
, EqualsValueClause
, 마지막으로 ObjectCreationExpression
이 있습니다. 노드의 Syntax Visualizer 트리를 클릭하면 편집기 창의 구문이 강조 표시되어 해당 노드가 나타내는 코드를 표시합니다. SyntaxNode 하위 형식의 이름은 C# 문법에 사용된 이름과 일치합니다.
분석기 프로젝트 만들기
주 메뉴에서 파일>새로 만들기>프로젝트를 선택합니다. 새 프로젝트 대화 상자의 왼쪽 탐색 메뉴에 있는 C# 프로젝트에서 확장성을 선택하고 오른쪽 창에서 코드 수정이 있는 분석기 프로젝트 템플릿을 선택합니다. 이름을 입력하고 대화 상자를 확인합니다.
템플릿은 DiagnosticAnalyzer.cs 파일을 엽니다. 편집기 버퍼 탭을 선택합니다. 이 파일에는 (Roslyn API 형식)에서 DiagnosticAnalyzer
파생된 분석기 클래스(프로젝트에서 지정한 이름으로 형성됨)가 있습니다. 새 클래스에는 컴파일러가 분석기를 검색하고 로드할 수 있도록 분석기가 C# 언어와 관련이 있음을 선언하는 DiagnosticAnalyzerAttribute
가 있습니다.
[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
문에서 코드가 사용하는 기호를 보고할 수 있습니다.
Syntax Visualizer를 사용하여 컴파일러가 ObjectCreationExpression을 처리할 때 호출되기를 원한다는 것을 배웠습니다. 이 코드를 사용하여 콜백을 설정합니다.
context.RegisterSyntaxNodeAction(c => AnalyzeObjectCreation(c),
SyntaxKind.ObjectCreationExpression);
구문 노드에 등록하고 개체 만들기 구문 노드만 필터링합니다. 일반적으로 분석기 작성자는 작업을 등록할 때 람다를 사용하므로 분석기를 상태 비저장 상태로 유지하는 데 도움이 됩니다. 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를 설치한 후 처음 시작할 때와 유사한 여러 초기화를 수행합니다.
콘솔 프로젝트를 만든 다음 콘솔 애플리케이션 주 메서드에 배열 코드를 입력합니다.
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);
변경할 수 없는 NuGet 패키지를 가져와 코드에 using
문을 추가해야 하므로 ImmutableArray
이 있는 코드 줄에는 물결선이 있습니다. 솔루션 탐색기 프로젝트 노드에서 오른쪽 포인터 단추를 누르고 NuGet 패키지 관리를 선택합니다. NuGet 관리자에서 검색 상자에 “Immutable”을 입력하고 왼쪽 창에서 System.Collections.Immutable 항목을 선택합니다(Microsoft.Bcl.Immutable는 선택하지 않음). 오른쪽 창에서 설치 단추를 누릅니다. 패키지를 설치하면 프로젝트 참조에 대한 참조가 추가됩니다.
ImmutableArray
아래에 여전히 빨간색 물결선이 표시되므로 해당 식별자에 캐럿을 배치하고 Ctrl+.(마침표)을 눌러 제안된 수정 메뉴를 표시하고 적절한 using
문을 추가하도록 선택합니다.
계속하려면 깨끗한 상태로 전환하려면 지금은 Visual Studio의 두 번째 인스턴스를 모두 저장하고 닫기하세요.
편집을 사용하여 분석기를 종료하고 계속
Visual Studio의 첫 번째 인스턴스에서 첫 번째 줄에 캐럿이 있는 F9를 눌러 AnalyzeObjectCreation
메서드의 시작 부분에 중단점을 설정합니다.
F5 키를 눌러 분석기를 다시 시작하고 Visual Studio의 두 번째 인스턴스에서 마지막으로 만든 콘솔 애플리케이션을 다시 엽니다.
Roslyn 컴파일러가 개체 생성 식을 보고 분석기를 호출했으므로 중단점에서 Visual Studio의 첫 번째 인스턴스로 돌아갑니다.
개체 만들기 노드를 가져옵니다. F10을 눌러 objectCreation
변수를 설정하는 줄을 건너뛰고 직접 실행 창에서 "objectCreation.ToString()"
식을 평가합니다. 변수가 가리키는 구문 노드가 찾고 있는 코드인 "new ImmutableArray<int>()"
임을 알 수 있습니다.
ImmutableArray<T> 형식 개체를 가져옵니다. 생성되는 형식이 ImmutableArray인지 확인해야 합니다. 먼저 이 형식을 나타내는 개체를 가져옵니다. 의미 체계 모델을 사용하여 유형을 확인하고 정확히 올바른 유형인지 확인하고 ToString()
의 문자열을 비교하지 않습니다. 함수 끝에 다음 코드 줄을 입력합니다.
var immutableArrayOfTType =
context.SemanticModel
.Compilation
.GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1");
백틱(`) 및 제네릭 매개 변수 수를 사용하여 메타데이터에서 제네릭 형식을 지정합니다. 이것이 메타데이터 이름에 “...ImmutableArray<T>”가 표시되지 않는 이유입니다.
의미 체계 모델에는 기호, 데이터 흐름, 가변 수명 등에 대한 질문을 할 수 있는 많은 유용한 항목이 있습니다. Roslyn은 다양한 엔지니어링 이유(성능, 잘못된 코드 모델링 등)를 위해 구문 노드를 의미 체계 모델과 구분합니다. 컴파일 모델에서 정확한 비교를 위해 참조에 포함된 정보를 조회하려고 합니다.
편집기 창의 왼쪽에서 노란색 실행 포인터를 끌 수 있습니다. objectCreation
변수를 설정하는 줄까지 끌어 F10을 사용하여 새 코드 줄을 건너뜁니다. 변수 immutableArrayOfType
위로 마우스 포인터를 가져가면 의미 체계 모델에서 정확한 형식을 찾았음을 알 수 있습니다.
개체 만들기 식의 형식을 가져옵니다. “Type”은 이 문서에서 몇 가지 방식으로 사용되지만, 이는 “new 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))
{}
진단을 보고합니다. 진단을 보고하는 것은 매우 쉽습니다. 초기화 메서드 앞에 정의된 프로젝트 템플릿에서 만든 규칙을 사용합니다. 코드의 이 상황은 오류이므로 DiagnosticSeverity.Warning
(녹색 물결선)을 DiagnosticSeverity.Error
(빨간색 물결선)로 바꾸도록 규칙을 초기화한 줄을 변경할 수 있습니다. 나머지 규칙은 연습 시작 부분에서 편집한 리소스에서 초기화됩니다. 또한 개체 만들기 식의 형식 사양 위치인 물결선의 위치를 보고해야 합니다. 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
특성으로 주석을 달아야 하며 LanguageNames
열거형을 해결하려면 using
문을 추가해야 합니다. 다음 코드가 포함된 클래스 파일이 있어야 합니다.
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은 문자열인 이러한 식별자를 일치시켜 진단 및 수정 사항을 함께 제공합니다. 프로젝트 템플릿에서 진단 ID를 생성했으며 자유롭게 변경할 수 있습니다. 속성의 코드는 분석기 클래스의 ID만 반환합니다.
RegisterCodeFixAsync 메서드는 컨텍스트를 사용합니다. 코드 수정이 여러 진단에 적용되거나 코드 줄에 둘 이상의 문제가 있을 수 있으므로 컨텍스트가 중요합니다. 메서드 본문에 “context.”를 입력하면 IntelliSense 완성 목록에 몇 가지 유용한 멤버가 표시됩니다. 수정 사항을 취소하려는 항목이 있는지 확인할 수 있는 CancellationToken 멤버가 있습니다. 유용한 멤버가 많고 프로젝트 및 솔루션 모델 개체에 연결할 수 있는 Document 멤버가 있습니다. 진단을 보고할 때 지정된 코드 위치의 시작 및 끝인 Span 멤버가 있습니다.
메서드를 비동기로 만듭니다. 가장 먼저 해야 할 일은 생성된 메서드 선언을 async
메서드로 수정하는 것입니다. 메서드가 Task
를 반환하더라도 추상 클래스 구현을 제거하기 위한 코드 수정에는 async
키워드가 포함되어 있지 않습니다.
구문 트리의 루트를 가져옵니다. 코드를 수정하려면 코드 수정 사항이 변경된 새 구문 트리를 생성해야 합니다. GetSyntaxRootAsync
를 호출하려면 컨텍스트에서 Document
가 필요합니다. 이것은 디스크에서 파일 가져오기, 구문 분석, Roslyn 코드 모델 빌드를 포함하여 구문 트리를 가져오는 알 수 없는 작업이 있기 때문에 비동기 메서드입니다. Visual Studio UI는 이 시간 동안 응답해야 하며 async
를 사용하면 활성화됩니다. 메서드의 코드 줄을 다음으로 바꿉니다.
var root = await context.Document
.GetSyntaxRootAsync(context.CancellationToken);
문제가 있는 노드를 찾습니다. 컨텍스트의 범위를 전달하지만 찾은 노드가 변경해야 하는 코드가 아닐 수 있습니다. 보고된 진단은 형식 식별자(물결선이 속한 위치)에 대한 범위만 제공했지만 시작의 new
키워드와 끝에 있는 괄호를 포함하여 전체 개체 만들기 식을 바꿔야 합니다. 메서드에 다음 코드를 추가하고 Ctrl+.를 사용하여 ObjectCreationExpressionSyntax
에 대한 using
문을 추가합니다.
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+.를 다시 사용하여 이 메서드 스텁을 생성합니다.
추가한 이 마지막 코드 조각은 CodeAction
및 발견된 문제 종류에 대한 진단 ID를 전달하여 코드 수정 사항을 등록합니다. 이 예제에서는 이 코드에서 수정 사항을 제공하는 진단 ID가 하나뿐이므로 진단 ID 배열의 첫 번째 요소를 전달할 수 있습니다. CodeAction
을 만들 때 전구 UI가 코드 수정에 대한 설명으로 사용해야 하는 텍스트를 전달합니다. 또한 CancellationToken을 사용하고 새 문서를 반환하는 함수를 전달합니다. 새 문서에는 ImmutableArray.Empty
을 호출하는 패치된 코드가 포함된 새 구문 트리가 있습니다. 이 코드 조각은 람다를 사용하므로 objectCreation 노드와 컨텍스트의 문서를 닫을 수 있습니다.
새 구문 트리를 생성합니다. 이전에 스텁을 생성한 ChangeToImmutableArrayEmpty
메서드에 코드 줄 ImmutableArray<int>.Empty;
를 입력합니다. Syntax Visualizer 도구 창을 다시 보면 이 구문이 SimpleMemberAccessExpression 노드임을 알 수 있습니다. 이것이 바로 이 메서드가 새 문서를 생성하고 반환하는 데 필요한 것입니다.
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# 파일과 Visual Studio 전구 UI에 표시되는 코드 수정 사항을 구현하는 C# 파일이 있습니다. 완성된 코드에는 ImmutableArray<T> 형식 개체를 계속해서 가져오지 않도록 좀 더 추상화되어 있습니다. 중첩된 등록 작업을 사용하여 하위 작업(개체 생성 분석 및 컬렉션 초기화 분석)이 실행될 때마다 사용할 수 있는 컨텍스트에 형식 개체를 저장합니다.