성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# 12 - Interceptor (컴파일 시에 메서드 호출 재작성)</h1> <p> 오늘(KST 2023-11-15) 공개된 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#interceptors'>C# 12 문서에는 이 기능</a>이 아직 정식은 아니고 "experimental feature"에 해당한다고 합니다. 따라서 향후에는 변경/삭제될 수 있음을 감안해야 합니다. <br /> <br /> 현재 C# 12 정식 버전에 해당하는 내용으로 업데이트했지만 향후 바뀔 수 있으므로 구현 전에는 <a target='tab' href='https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md'>관련 문서</a>를 참조하길 권장합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 아래와 같은 글이 있군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET 8 Interceptors ; <a target='tab' href='https://khalidabuhakmeh.com/dotnet-8-interceptors'>https://khalidabuhakmeh.com/dotnet-8-interceptors</a> </pre> <br /> 제목으로 보면 마치 ".NET 8"과 연관이 있는 듯하지만, 실제로는 <a target='tab' href='https://learn.microsoft.com/ko-kr/dotnet/csharp/whats-new/csharp-12#interceptors'>공식 문서</a>에도 소개됐듯이 C# 12의 신규 기능입니다.<br /> <br /> <strike>실습하려면, <a target='tab' href='https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md#c-120'>Visual Studio 17.7 preview 3부터 적용</a>되었다고 나오므로 현재(2023-09-08) 시점의 정식 버전이 17.7.0이므로 역시 Visual Studio Preview 버전에서만 실행해 볼 수 있습니다.</strike><br /> <br /> 자, 그럼 간단하게 테스트를 해볼까요? ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 우선 사전 작업으로 특성 하나를 추가해야 합니다. <strike>현재 Visual Studio 2022 Preview를 최신 버전으로 업데이트하면 "8.0.100-preview.7.23376.3" 버전의 .NET SDK가 설치되는데요, C# 12의 가로채기(intercept) 기술은 특성(Attribute)를 기반으로 동작하는 반면, 아직 8.0 Preview에는 관련 타입이 정의돼 있지 않아</strike> 다음과 같이 소스 코드에 명시적으로 추가를 해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > namespace System.Runtime.CompilerServices; [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] public sealed class InterceptsLocationAttribute : Attribute { public InterceptsLocationAttribute(string filePath, int line, int character) { } } </pre> <br /> 그다음, C# 콘솔 프로젝트를 하나 생성하고 csproj의 <strike>Features 속성에 InterceptorsPreview 값을 하나 추가합니다.</strike> InterceptorsPreviewNamespaces 속성에 InterceptsLocation 특성을 사용할 코드의 네임스페이스를 명시합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <strike><span style='color: blue; font-weight: bold'><Features>InterceptorsPreview</Features></span></strike> <!-- .NET 7 ~ .NET 8.0.100-preview.6.23330.14 까지만 동작 --> <span style='color: blue; font-weight: bold'><InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);ClassLib1.Experimental</InterceptorsPreviewNamespaces></span> </PropertyGroup> </Project> </pre> <br /> 준비는 이게 끝입니다. 다음은 일반적인 코딩을 하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Runtime.CompilerServices; namespace ConsoleApp2 { internal class Program { static void Main(string[] args) { Program.ProgramMethod(); // |- 위의 "P" 위치가 <span style='color: blue; font-weight: bold'>9번째 줄, 21번째 칼럼</span>에 위치 } static void ProgramMethod() { Console.WriteLine("Program Method"); } } } /* 현재 상태의 출력 결과: Program Method */ </pre> <br /> 위의 코드에서 우리가 가로채고 싶은 것은 "Program.ProgramMethod()" 호출입니다. 이를 위해 C# 12는 호출이 되는 대상 파일의 "절대 경로", "호출 라인", "호출 칼럼"의 정보를 정확하게 기입한 새로운 메서드를 정의할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > namespace ClassLib1.Experimental; // csproj의 InterceptorsPreviewNamespaces 값에 추가했던 네임스페이스 class My { [InterceptsLocation("C:\\temp\\ConsoleApp1\\ConsoleApp2\\Program.cs", <span style='color: blue; font-weight: bold'>line: 9, character: 21</span>)] public static void MyMethod() => Console.WriteLine("My Method"); } </pre> <br /> 느낌이 오시나요? 말 그대로 InterceptsLocation에 지정한 파일, 라인, 칼럼의 메서드 호출을 재작성하겠다는 것입니다.<br /> <br /> 따라서 위의 코드를 실행하면 이제 출력은 "My Method"로 바뀝니다. 당연하겠지만, 만약 여러분이 Main 코드를 다음과 같이 바꾸기라도 하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static void Main(string[] args) { Console.WriteLine("TEST"); // 이 코드의 삽입으로 인해, Program.ProgramMethod(); // |- 위의 "P" 위치가 10번째 줄, 21번째 칼럼에 위치 } </pre> <br /> InterceptsLocation에 기록된 정보와 달라지므로 호출 코드는 재작성되지 않습니다. 따라서, 만약 가로챈 메서드가 있다면 기존 소스 코드를 바꿀 때 주의 깊게 InterceptsLocation 특성의 전달 값도 동기화시켜야 합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 혹시 위의 코드를 보고 나서 한 가지 의문이 생기지 않나요? ^^ 정적 메서드는 그렇게 외부 메서드로 뺼 수 있지만 인스턴스 메서드는 어떻게 재작성할 수 있을까요? 이에 대해서는 문서를 참고할 수 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Interceptors ; <a target='tab' href='https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md'>https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md</a> </pre> <br /> 보시면, 인스턴스 메서드는 C#답게 ^^ 확장 메서드로 풀어내고 있습니다. 그렇다는 것은, public 멤버만 접근 가능한 상태로 코딩이 가능한 상황에서만 재작성할 수 있다는 것을 의미합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그냥 보면, 뭐 이런 현실성 없는, 그다지 쓸만한 데가 마땅치 않은 메서드 재작성 기능이 있나... 싶을 텐데요, 제가 보기에도 일반적인 프로젝트에서 딱히 쓸 필요는 없어 보입니다.<br /> <br /> 단지, 이게 나름 의미 있는 사용처가 있는데요, 바로 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/12985'>소스 코드 생성기(Source Generator)</a>"입니다.<br /> <br /> Source Generator의 대표적인 한계가 바로 "기존 소스 코드의 변경은 불가능"하다는 점인데요, 바로 이 한계를 Interceptor가 (제한적이나마) 가능하게 해줍니다. 게다가 Source Generator의 특성상, 컴파일 시점에 구문 분석을 통해 대상 메서드의 정확한 파일/라인/칼럼 위치를 알 수 있으므로 위의 예제에서 My 클래스의 소스 코드는 원래 Source Generator 등이 생성해야 할 과제인 것입니다.<br /> <br /> 그런 관점에서 다시 보면, "소스 코드 변경"을 할 수 없는 Roslyn의 Plug-in 정책을 위반하지 않으면서도 나름대로의 확장을 가능하게 한 기특한 생각이라고 봐도 좋을 것입니다. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2705
(왼쪽의 숫자를 입력해야 합니다.)