다음을 통해 공유


ImmutableArrays용 Roslyn 분석기 및 코드 인식 라이브러리

.NET 컴파일러 플랫폼("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 컴파일러 플랫폼("Roslyn") SDK. 기본 메뉴 파일>>프로젝트로 이동하여 왼쪽 탐색 창에서 C# 을 선택한 다음 확장성을 선택하여 이 SDK를 설치할 수도 있습니다. ".NET 컴파일러 플랫폼 SDK 다운로드" 이동 경로 프로젝트 템플릿을 선택하면 SDK를 다운로드하고 설치하라는 메시지가 표시됩니다. 이 SDK에는 Roslyn 구문 시각화 도우미가 포함됩니다. 이 유용한 도구는 분석기에서 찾아야 하는 코드 모델 형식을 파악하는 데 도움이 됩니다. 분석기 인프라는 특정 코드 모델 형식에 대한 코드를 호출하므로 코드는 필요한 경우에만 실행되며 관련 코드 분석에만 집중할 수 있습니다.

문제가 뭔가요?

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.LengthImmutableArray 구조체에 기본 스토리지 배열이 없으므로 런타임 null 역참조 오류가 발생합니다. 빈 ImmutableArray를 만드는 올바른 방법은 .입니다 ImmutableArray<int>.Empty.

컬렉션 이니셜라이저의 오류는 메서드가 ImmutableArray.Add 호출할 때마다 새 인스턴스를 반환하기 때문에 발생합니다. ImmutableArrays는 변경되지 않으므로 새 요소를 추가할 때 새 ImmutableArray 개체를 다시 가져옵니다(성능상의 이유로 기존 ImmutableArray와 스토리지를 공유할 수 있음). b2 다섯 번 Add() 호출 b2 하기 전에 첫 번째 ImmutableArray를 가리키므로 기본 ImmutableArray입니다. 또한 길이를 호출하면 null 역참조 오류가 발생합니다. 수동으로 Add를 호출하지 않고 ImmutableArray를 초기화하는 올바른 방법은 .를 사용하는 ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5})것입니다.

분석기를 트리거하는 관련 구문 노드 형식 찾기

분석기 빌드를 시작하려면 먼저 찾아야 하는 SyntaxNode 유형을 파악합니다. 구문 시각화 도우미보기 메뉴에 있는 기타 WindowsRoslyn 구문 시각화 도우미에서 시작합니다.

b1을 선언하는 줄에 편집기의 커서를 놓습니다. 구문 시각화 도우미는 당신이 구문 트리의 LocalDeclarationStatement 노드에 있다는 것을 보여줍니다. 이 노드에는 VariableDeclaration이 있고, 여기에 VariableDeclarator이 있으며, 그 안에 EqualsValueClause가 있고, 마지막으로 ObjectCreationExpression가 있습니다. 노드의 구문 시각화 도우미 트리를 클릭하면 편집기 창의 구문이 강조 표시되어 해당 노드가 나타내는 코드를 표시합니다. SyntaxNode 하위 형식의 이름은 C# 문법에 사용되는 이름과 일치합니다.

분석기 프로젝트 만들기

주 메뉴에서 파일>새로 만들기>프로젝트를 선택합니다. 새 프로젝트 대화 상자의 왼쪽 탐색 모음에 있는 C# 프로젝트 아래에서 확장성을 선택하고 오른쪽 창에서 코드 수정 프로젝트 템플릿이 있는 분석기를 선택합니다. 이름을 입력하고 대화 상자를 확인합니다.

템플릿이 DiagnosticAnalyzer.cs 파일을 엽니다. 편집기 버퍼 탭을 선택합니다. 이 파일에는 프로젝트에서 지정한 이름을 기반으로 형성된 분석기 클래스가 있으며, 이는 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);

구문 노드에 등록하고 개체 만들기 구문 노드만 필터링합니다. 규칙에 따라 분석기 작성자는 작업을 등록할 때 람다를 사용하므로 분석기를 상태 비저장 상태로 유지하는 데 도움이 됩니다. 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를 설치한 후 처음 시작할 때와 비슷한 몇 가지 초기화를 수행합니다.

콘솔 프로젝트를 만든 다음 콘솔 애플리케이션 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 관리자에서 검색 상자에 "변경할 수 없음"을 입력하고 왼쪽 창에서 System.Collections.Immutable ( Microsoft.Bcl.Immutable 선택 안 함) 항목을 선택하고 오른쪽 창에서 설치 단추를 누릅니다. 패키지를 설치하면 프로젝트 참조에 대한 참조가 추가됩니다.

여전히 빨간색 물결선이 나타나면, 해당 식별자에 커서를 놓고 ImmutableArray키와 + (.)를 눌러 제안된 수정 메뉴를 표시하고 적절한 문을 추가하도록 선택하십시오.

지금 Visual Studio의 두 번째 인스턴스를 모두 저장 후 닫아 다음 단계를 깨끗하게 준비합니다.

편집을 사용하여 분석기를 완료하고 계속

Visual Studio의 첫 번째 인스턴스에서 첫 번째 줄에 캐럿이 있는 AnalyzeObjectCreation 키를 눌러 메서드의 시작 부분에 중단점을 설정합니다.

F5를 사용하여 분석기를 다시 시작하고 Visual Studio의 두 번째 인스턴스에서 마지막으로 만든 콘솔 애플리케이션을 다시 엽니다.

중단점에서 Visual Studio의 첫 번째 인스턴스로 돌아가는 이유는 Roslyn 컴파일러가 개체 생성 식을 인식한 뒤 분석기를 호출했기 때문입니다.

개체 만들기 노드를 가져옵니다. objectCreation 키를 눌러 을 넘고 Immediate Window에서 "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위로 가져가면 의미 체계 모델에서 정확한 형식을 찾은 것을 볼 수 있습니다.

개체 만들기 식의 형식을 가져옵니다. "Type"은 이 문서에서 몇 가지 방법으로 사용되지만 이는 "새 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 특성으로 주석을 추가해야 하며, using 열거형을 해결하기 위해 LanguageNames 문장을 추가해야 합니다. 다음 코드가 포함된 클래스 파일이 있어야 합니다.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;

namespace ImmutableArrayAnalyzer
{
    [ExportCodeFixProvider(LanguageNames.CSharp)]
    class BuildCodeFixProvider : CodeFixProvider
    {}

파생 멤버를 임시 구현합니다. 이제 편집기 캐럿을 식별자에 CodeFixProvider 배치하고 Ctrl+. (마침표)를 눌러 이 추상 기본 클래스에 대한 구현을 생성합니다. 이렇게 하면 속성과 메서드가 생성됩니다.

속성을 구현합니다. FixableDiagnosticIds 속성의 본문을 다음 코드로 채우십시오.

return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);

Roslyn은 문자열인 이러한 식별자를 일치시켜 진단 및 수정 사항을 함께 제공합니다. 프로젝트 템플릿에서 진단 ID를 생성했으며 자유롭게 변경할 수 있습니다. 속성의 코드는 분석기 클래스의 ID만 반환합니다.

RegisterCodeFixAsync 메서드는 컨텍스트를 사용합니다. 코드 수정이 여러 진단에 적용되거나 코드 줄에 둘 이상의 문제가 있을 수 있으므로 컨텍스트가 중요합니다. 메서드 본문에 "context"를 입력하면 IntelliSense 완성 목록에 몇 가지 유용한 멤버가 표시됩니다. Fix를 취소하려는 항목이 있는지 확인할 수 있는 CancellationToken 멤버가 있습니다. 유용한 멤버가 많고 프로젝트 및 솔루션 모델 개체에 연결할 수 있는 Document 멤버가 있습니다. 진단을 보고할 때 지정된 코드 위치의 시작 및 끝인 Span 멤버가 있습니다.

메서드를 비동기로 만듭니다. 가장 먼저 해야 할 일은 생성된 메서드 선언을 메서드로 수정하는 것입니다 async . 추상 클래스의 구현을 스텁아웃하기 위한 코드 수정에는 메서드가 async 반환 Task되는 경우에도 키워드가 포함되지 않습니다.

구문 트리의 루트를 가져옵니다. 코드를 수정하려면 코드 수정 내용이 변경된 새 구문 트리를 생성해야 합니다. 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> (마침표)를 사용할 수 있다는 것을 알게 될 것입니다. 코드 수정 공급자는 문제가 있을 때만 실행되므로 찾고 있던 개체 만들기 식이 있다고 가정할 수 있습니다. 컨텍스트 매개 변수에서 메서드의 끝에 RegisterCodeFixAsync 다음 코드를 추가하여 새 코드 수정을 등록할 수 있습니다.

context.RegisterCodeFix(
            CodeAction.Create("Use ImmutableArray<T>.Empty",
                              c => ChangeToImmutableArrayEmpty(objectCreation,
                                                               context.Document,
                                                               c)),
            context.Diagnostics[0]);

편집기 캐럿을 식별자에 CodeAction 배치한 다음 Ctrl+. (마침표)을 사용하여 이 형식에 적합한 using 문장을 추가해야 합니다.

그런 다음 편집기의 캐럿을 ChangeToImmutableArrayEmpty 식별자에 놓고 다시 Ctrl+ 키를 눌러 이 메서드 스텁을 생성할 수 있습니다.

추가한 이 마지막 코드 조각은 발견된 문제의 종류에 대한 진단 ID와 a CodeAction 를 전달하여 코드 수정을 등록합니다. 이 예제에서는 이 코드에서 수정 사항을 제공하는 진단 ID가 하나뿐이므로 진단 ID 배열의 첫 번째 요소를 전달할 수 있습니다. 코드 수정을 위한 전구 UI 설명으로 사용될 텍스트를 만들 때 CodeAction에 전달합니다. CancellationToken을 사용하고 새 문서를 반환하는 함수도 전달합니다. 새 문서에는 ImmutableArray.Empty을(를) 호출하는 패치된 코드가 포함된 새 구문 트리가 있습니다. 이 코드 조각은 objectCreation 노드와 컨텍스트의 문서를 캡처할 수 있도록 람다 함수를 사용합니다.

새 구문 트리를 생성합니다. 생성한 스텁의 ChangeToImmutableArrayEmpty 메서드에 다음 코드 줄을 입력합니다: ImmutableArray<int>.Empty;. 구문 시각화 도우미 도구 창을 다시 보면 이 구문이 SimpleMemberAccessExpression 노드임을 확인할 수 있습니다. 이것이 바로 이 메서드가 새 문서를 생성하고 반환하는 데 필요한 것입니다.

첫 번째 ChangeToImmutableArrayEmpty의 변경 사항은 코드 생성기가 메서드가 비동기라고 가정할 수 없으므로 async 이전에 Task<Document>을 추가하는 것입니다.

메서드가 다음과 비슷하게 보이도록 본문을 다음 코드로 채웁니다.

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은 이 작업을 자동으로 만듭니다.

Pro 팁: Visual Studio의 두 번째 인스턴스를 시작하고 코드 수정으로 전구가 표시되지 않는 경우 Visual Studio 구성 요소 캐시를 지워야 할 수 있습니다. 캐시를 지우면 Visual Studio에서 구성 요소를 다시 검사하므로 Visual Studio에서 최신 구성 요소를 선택해야 합니다. 먼저 Visual Studio의 두 번째 인스턴스를 종료합니다. 그런 다음 Windows 탐색기에서 \Microsoft\VisualStudio\16.0Roslyn\%LOCALAPPDATA% 이동합니다. ("16.0"은 Visual Studio를 사용하여 버전에서 버전으로 변경됩니다.) Subdirectory ComponentModelCache를 삭제합니다.

비디오 대화 및 코드 프로젝트 완료

여기에서 완성된 모든 코드를 볼 수 있습니다. DoNotUseImmutableArrayCollectionInitializerDoNotUseImmutableArrayCtor 하위 폴더에는 각각 문제를 찾기 위한 C# 파일과 Visual Studio 전구 UI에 표시되는 코드 수정을 구현하는 C# 파일이 있습니다. 완성된 코드에는 ImmutableArray<T> 형식 개체를 반복해서 가져오는 것을 방지하기 위해 좀 더 추상화가 있습니다. 중첩된 등록된 작업을 사용하여 하위 작업(개체 만들기 분석 및 컬렉션 초기화 분석)이 실행될 때마다 사용할 수 있는 컨텍스트에 형식 개체를 저장합니다.