Microsoft MVP성태의 닷넷 이야기
.NET Framework: 908. C# - Source Generator 소개 [링크 복사], [링크+제목 복사]
조회: 696
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

C# - Source Generator 소개

다음과 같은 소식이 있군요. ^^

Introducing C# Source Generators
; https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/

위의 글에서도 잘 설명하고 있지만, 그대로 따라 해 보면서 한글로 다시 소개를 하겠습니다. ^^ 참고로, 저 글을 쓸 당시에는 비주얼 스튜디오에서 지원하지 않았지만, 현재 (16.6.1) 버전에서는 지원하므로 별도의 Preview 버전을 설치하지 않아도 무방합니다. (또한 위의 글에 따르면 .NET 5 Preview도 필요하다는데 제가 테스트한 바로는 .NET Core 3.1.2 환경에서 잘 동작했습니다.)




Source Generator가 뭔지는 위의 글에 포함된 그림에서 잘 설명하고 있습니다.

cs_src_gen_1.png

그러니까, 컴파일 시점에 개발자가 임의대로 다시 소스 코드를 구성해 (단, 기존 소스 코드의 변경은 불가능하지만) 끼워 넣을 수 있는 여지를 준 것입니다. 말로만 하면 심심하니 실습을 해보겠습니다.

우선, 콘솔 프로젝트를 하나 만들고 아래의 예제 코드를 입력합니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            HelloWorldGenerated.HelloWorld.SayHello();
        }
    }
}

당연히, 위의 단계까지는 빌드하면 "error CS0103: The name 'HelloWorldGenerated' does not exist in the current context" 오류가 발생합니다. 이제부터 할 일은, 컴파일 시에 HelloWorldGenerated.HelloWorld 타입을 담는 소스 코드를 끼워 넣으면 되는데, 이 작업을 일종의 plug-in처럼 처리하게 됩니다. 이를 위해 ".NET Standard Library" 유형의 프로젝트를 생성하고 다음과 같이 .csproj 파일을 수정합니다.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.6.0" PrivateAssets="all" />
    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.0.0" PrivateAssets="all" />
  </ItemGroup>

</Project>

또는 NuGet을 통해 패키지 참조를 해도 됩니다.

Install-Package Microsoft.CodeAnalysis.Analyzers -Version 3.0.0
Install-Package Microsoft.CodeAnalysis.CSharp.Workspaces -Version 3.6.0

이후 ISourceGenerator 인터페이스를 상속받아 "Generator" 특성이 붙은 타입을 정의하면,

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Text;

namespace HelloGenerator
{
    [Generator]
    public class MySourceGenerator : ISourceGenerator
    {
        public void Execute(SourceGeneratorContext context)
        {
            // begin creating the source we'll inject into the users compilation
            var sourceBuilder = new StringBuilder(@"
using System;
namespace HelloWorldGenerated
{
    public static class HelloWorld
    {
        public static void SayHello() 
        {
            Console.WriteLine(""Hello from generated code!"");
            Console.WriteLine(""The following syntax trees existed in the compilation that created this program:"");
");

            // using the context, get a list of syntax trees in the users compilation
            var syntaxTrees = context.Compilation.SyntaxTrees;

            // add the filepath of each tree to the class we're building
            foreach (SyntaxTree tree in syntaxTrees)
            {
                sourceBuilder.AppendLine($@"Console.WriteLine(@"" - {tree.FilePath}"");");
            }

            // finish creating the source to inject
            sourceBuilder.Append(@"
        }
    }
}");

            // inject the created source into the users compilation
            context.AddSource("helloWorldGenerator", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
        }

        public void Initialize(InitializationContext context)
        {
            // No initialization required for this one
        }
    }
}

Roslyn의 컴파일 단계에 참여할 수 있는 plug-in이 완성됩니다. 남은 작업은, 위의 plug-in을 사용할 첫 번째 콘솔 프로젝트에 참조 추가를 하고, Preview 단계의 C# 컴파일러를 사용하도록 지정하면 되는데,

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <LangVersion>preview</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\HelloGenerator\HelloGenerator.csproj" />
  </ItemGroup>

</Project>

특별히, plug-in이라는 점을 명시하기 위해 OutputItemType을 Analyzer로 설정하고, 또한 실제 실행 시 필요한 어셈블리는 아니므로 ReferenceOutputAssembly를 false로 지정해 줍니다.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <LangVersion>preview</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\HelloGenerator\HelloGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
  </ItemGroup>

</Project>

이렇게 하고 빌드하면, 컴파일 시점에 ISourceGenerator.Execute 메서드가 호출되고 그로 인해 HelloWorldGenerated.HelloWorld 타입이 정의되어 함께 빌드에 참여하게 되므로 예제 콘솔 프로젝트가 정상적으로 빌드가 됩니다.

그래서 예제 코드를 실행하면 다음과 같은 식의 출력을 볼 수 있습니다.

Hello from generated code!
The following syntax trees existed in the compilation that created this program:
 - D:\temp\ConsoleApp1\ConsoleApp1\Program.cs
 - D:\temp\ConsoleApp1\ConsoleApp1\obj\Debug\netcoreapp3.1\.NETCoreApp,Version=v3.1.AssemblyAttributes.cs
 - D:\temp\ConsoleApp1\ConsoleApp1\obj\Debug\netcoreapp3.1\ConsoleApp1.AssemblyInfo.cs

결과를 보면 알 수 있겠지만, 이러한 출력 결과는 일반적인 C#의 기존 코딩 방법으로는 할 수 없는 작업물입니다.

(첨부 파일은 이 글의 완전한 예제 프로젝트를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]





[최초 등록일: ]
[최종 수정일: 6/10/2020 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer@outlook.com

비밀번호

댓글 쓴 사람
 



2020-07-13 05시32분
T4(TextTemplate) 와 비교해봤을 때 궁금한 점이 있습니다. 자동생성된 코드를 디버깅할 일이 있으면 T4같은 경우는 실행했을 때 tt 파일로 생성되는 cs 파일을 찾아 확인할 수 있는데요. SourceGenerator에서는 ReferenceOutputAssembly="true" 하여 생성되는 dll 을 decompile 하는 방법 밖에는 없는지요?
kernel
2020-07-13 06시46분
[kernel] 추후 제공할 예정이라고 하네요. ^^;
[손님]
2020-08-26 02시11분
New C# Source Generator Samples
 - CSV Generator
 - Mustache Generator
; https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/
정성태

1  2  3  4  5  [6]  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
12252정성태7/2/2020608.NET Framework: 921. C# - I/O 스레드를 사용한 비동기 소켓 서버/클라이언트파일 다운로드2
12251정성태7/1/2020453.NET Framework: 920. C# - 파일의 비동기 처리 유무에 따른 스레드 상황파일 다운로드2
12250정성태7/1/2020881.NET Framework: 919. C# - 닷넷에서의 진정한 비동기 호출을 가능케 하는 I/O 스레드 사용법 [1]파일 다운로드1
12249정성태6/29/2020260오류 유형: 625. Microsoft SQL Server 2019 RC1 Setup - 설치 제거 시 Warning 26003 오류 발생
12248정성태6/29/2020240오류 유형: 624. SQL 서버 오류 - service-specific error code 17051
12247정성태6/29/2020485.NET Framework: 918. C# - 불린 형 상수를 반환값으로 포함하는 3항 연산자 사용 시 단축 표현 권장(IDE0075) [2]파일 다운로드1
12246정성태6/29/2020337.NET Framework: 917. C# - USB 관련 ETW(Event Tracing for Windows)를 이용한 키보드 입력을 감지하는 방법
12245정성태6/25/2020614.NET Framework: 916. C# - Task.Yield 사용법 (2) [2]파일 다운로드1
12244정성태8/28/2020440.NET Framework: 915. ETW(Event Tracing for Windows)를 이용한 닷넷 프로그램의 내부 이벤트 활용파일 다운로드1
12243정성태6/23/2020267VS.NET IDE: 147. Visual C++ 프로젝트 - .NET Core EXE를 "Debugger Type"으로 지원하는 기능 추가
12242정성태6/24/2020285오류 유형: 623. AADSTS90072 - User account '...' from identity provider 'live.com' does not exist in tenant 'Microsoft Services'
12241정성태6/26/2020519.NET Framework: 914. C# - Task.Yield 사용법파일 다운로드1
12240정성태6/23/2020399오류 유형: 622. 소켓 바인딩 시 "System.Net.Sockets.SocketException: An attempt was made to access a socket in a way forbidden by its access permissions" 오류 발생
12239정성태6/21/2020329Linux: 30. (윈도우라면 DLL에 속하는) .so 파일이 텍스트로 구성된 사례
12238정성태6/21/2020377.NET Framework: 913. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 라이브러리
12237정성태7/8/2020386.NET Framework: 912. 리눅스 환경의 .NET Core에서 "test".IndexOf("\0")가 0을 반환
12236정성태6/19/2020323오류 유형: 621. .NET Standard 대상으로 빌드 시 dynamic 예약어에서 컴파일 오류 - error CS0656: Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
12235정성태6/19/2020355오류 유형: 620. Windows 10 - Inaccessible boot device 블루 스크린
12234정성태6/19/2020295개발 환경 구성: 494. NuGet - nuspec의 패키지 스키마 버전(네임스페이스) 업데이트 방법
12233정성태6/19/2020259오류 유형: 619. SQL 서버 - The transaction log for database '...' is full due to 'LOG_BACKUP'. - 두 번째 이야기
12232정성태6/19/2020225오류 유형: 618. SharePoint - StoreBusyRetryLater 오류
12231정성태6/15/2020441.NET Framework: 911. Console/Service Application을 위한 SynchronizationContext - AsyncContext
12230정성태6/15/2020284오류 유형: 617. IMetaDataImport::GetMethodProps가 반환하는 IL 코드 주소(RVA) 문제
12229정성태6/13/2020568.NET Framework: 910. USB/IP PROJECT를 이용해 C#으로 USB Keyboard + Mouse 가상 장치 만들기 [1]
12228정성태6/12/2020406.NET Framework: 909. C# - Source Generator를 적용한 XmlCodeGenerator파일 다운로드1
12227정성태6/12/20203302오류 유형: 616. Visual Studio의 느린 업데이트 속도에 대한 원인 분석 [5]
1  2  3  4  5  [6]  7  8  9  10  11  12  13  14  15  ...