성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
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'>Roslyn 맛보기 - Roslyn Symbol / Binding API</h1> <p> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Roslyn 맛보기 (1) - <a target='tab' href='http://www.sysnet.pe.kr/2/0/1153'>C# 소스 코드를 스크립트 처럼 다루는 방법</a> Roslyn 맛보기 (2) - <a target='tab' href='http://www.sysnet.pe.kr/2/0/1154'>C# Interactive (1)</a> Roslyn 맛보기 (3) - <a target='tab' href='http://www.sysnet.pe.kr/2/0/1155'>C# Interactive (2)</a> Roslyn 맛보기 (4) - <a target='tab' href='http://www.sysnet.pe.kr/2/0/1156'>Roslyn Services APIs를 이용한 Code Issue 및 Code Action 기능 소개</a> Roslyn 맛보기 (5) - <a target='tab' href='http://www.sysnet.pe.kr/2/0/1157'>Syntax Analysis (Roslyn Syntax API)</a> Roslyn 맛보기 (6) - Roslyn Symbol / Binding API Roslyn 맛보기 (7) - <a target='tab' href='http://www.sysnet.pe.kr/2/0/1159'>SyntaxTree 조작</a> </pre> <br /> (이번 글은, "%PROGRAMFILES% (x86)\Microsoft Codename Roslyn CTP\Documentation\Getting Started - Semantic Analysis (CSharp).docx" 문서를 요약한 것입니다.)<br /> <br /> 지난번에는 "Syntax API"를 살펴봤었지요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Roslyn 맛보기 - Syntax Analysis (Roslyn Syntax API) ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1157'>http://www.sysnet.pe.kr/2/0/1157</a> </pre> <br /> 즉, C# Parser에 대해서 알아본 것인데, 이것만으로는 C# 소스 코드로부터 '의미있는 정보'를 얻어내는 데에는 아직 부족한 면이 있습니다. 왜냐하면, 아직 Binding 작업이 이뤄지지 않았기 때문입니다.<br /> <br /> 이쯤에서, 이야기를 더 진행하기에 앞서 Symbol과 Binding에 대해서 설명을 하는 것이 좋겠군요. ^^<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;' > static void Main(string[] args) { <span style='color: blue; font-weight: bold'>SyntaxTree tree = SyntaxTree.ParseCompilationUnit</span>( @"using System; using System.Collections.Generic; using System.Linq; namespace HelloWorld { class Program { static void Main(string[] args) { Console.WriteLine(""Hello, World!""); } } }"); var root = (CompilationUnitSyntax)tree.Root; } </pre> <br /> 위의 소스 코드를 가지고 하나의 질문을 해보겠습니다. 과연, ParseCompilationUnit 메서드를 이용하여 SyntaxTree를 생성해 낸 경우 해당 tree로부터 "Console"에 정의된 다른 메서드를 열람할 수 있을까요?<br /> <br /> 이 문제에 대한 답은 SyntaxTree.ParseCompilationUnit에 대한 '인자'를 살펴보면 알 수 있습니다. 보는 바와 같이, ParseCompilationUnit 메서드로는 Console을 정의하고 있는 어셈블리 정보가 전달되지 않았습니다. 즉, 이런 과정을 통해서 생성된 SyntaxTree는 말 그대로 "C# 문법"에 적합한 구문 분석 결과를 내놓는 역할만 합니다.<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;' > static void Main(string[] args) { <span style='color: blue; font-weight: bold'>SyntaxTree tree1</span> = SyntaxTree.ParseCompilationUnit( @"using System; using System.Collections; using System.Linq; class Program { static void Main(string[] args) { Console.WriteLine(""Hello, World!""); <span style='color: blue; font-weight: bold'>new MyClass().DoMethod();</span> } }"); <span style='color: blue; font-weight: bold'>SyntaxTree tree2</span> = SyntaxTree.ParseCompilationUnit( @"using System; using System.Collections; using System.Linq; <span style='color: blue; font-weight: bold'>public class MyClass</span> { public void DoMethod() { Console.WriteLine(""DoMethod!""); } }"); } </pre> <br /> 여기서도 질문을 해보겠습니다. tree1 인스턴스에서 사용된 "MyClass"가 과연 struct인지, class인지 tree1 자체에서 알 수 있는 방법이 있을까요? 역시 이번에도 SyntaxTree 내에 저장된 정보로는 알 수 없습니다.<br /> <br /> 자, 이제 감이 오시죠? ^^ 바로 이렇게 외부에 참조된 어셈블리의 메타데이터 또는 다른 소스 코드에 선언된 클래스들과 현재의 SyntaxTree를 '연결'하는 작업이 "Binding"입니다. 컴파일러 입장에서는 tree2 인스턴스로부터 "MyClass"가 class임을 인식하고 별도의 "Symbol Table"에 이 값을 저장해 둔 후, 다른 소스 코드에서 "MyClass"를 사용했을 때 "Symbol Table"로부터 그에 대한 정보를 연결시키는 작업이라고 볼 수 있습니다.<br /> <br /> 당연하겠지만, Roslyn에서는 이런 바인딩 작업을 위해 Compilation.Create 메서드를 제공해 주고 있습니다. 아래는 위의 2가지 SyntaxTree에 대해 사용한 예제 코드입니다.<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; using System.Collections.Generic; using System.Linq; using System.Text; using Roslyn.Compilers; using Roslyn.Compilers.CSharp; namespace SemanticsCS { class Program { static void Main(string[] args) { SyntaxTree tree1 = SyntaxTree.ParseCompilationUnit(...); SyntaxTree tree2 = SyntaxTree.ParseCompilationUnit(...); <span style='color: blue; font-weight: bold'> var systemCore = new AssemblyFileReference(typeof(System.Linq.Enumerable).Assembly.Location); var mscorlib = new AssemblyFileReference(typeof(object).Assembly.Location); var compilation = Compilation.Create("HelloWorld") .AddReferences(systemCore) .AddReferences(mscorlib) .AddSyntaxTrees(tree1) .AddSyntaxTrees(tree2); </span> } } } </pre> <br /> 예상한 데로, 소스 코드에서 사용되고 있는 Symbol들에 대한 메타데이터 정보를 얻을 수 있는 어셈블리 2개를 참조시켰고, tree1과 tree2에 대한 SyntaxTree를 모두 전달함으로써 MyClass에 대한 이름 풀이도 가능하게 했습니다.<br /> <br /> 이제부터는, Roslyn에서 제공되는 "Semantic" 관련 API를 통해서 SyntaxTree에 정의된 특정 이름에 대해 Binding 된 심벌 정보를 얻어낼 수 있습니다. 아래는 그 예입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>var model = compilation.GetSemanticModel(tree1);</span> var nameInfo = model.GetSemanticInfo(root.Usings[0].Name); foreach (var ns in (nameInfo.Symbol as NamespaceSymbol).GetNamespaceMembers()) { Console.WriteLine(ns.Name); } // 출력 결과: Security Reflection Diagnostics Configuration Linq Globalization StubHelpers IO Text Dynamic Deployment Collections Runtime Management Threading Resources </pre> <br /> 첫 번째 "using System;"에서 "System"에 대한 정보가 Binding 되었으므로, 이제는 System이 "mscorlib.dll"에 정의되었음을 알 수 있고 "System" 네임스페이스 하위에 정의된 서브 네임스페이스 정보까지 열람할 수 있게 된 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 정리해 보면, 여러분이 C# 소스 코드로부터 원하는 정보가 어떤 것이냐에 따라서 Syntax API만 사용해서 끝내거나, 아니면 Binding까지 시켜서 Binding API를 사용해야만 할 수도 있습니다. 이 차이를 알고 있어야 적절한 API를 선택할 수 있겠지요. ^^<br /> <br /> 마무리 하기 전에, Compilation.Create 작업까지 했으면 사실상 어셈블리 생성까지 이미 가능한 수준이 되었기 때문에 마저 '컴파일' 과정을 완료시켜 보면 다음과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var compilation = Compilation.Create("HelloWorld") .AddReferences(systemCore) .AddReferences(mscorlib) .AddSyntaxTrees(tree1) .AddSyntaxTrees(tree2); using (var stream = new MemoryStream()) { EmitResult compileResult = <span style='color: blue; font-weight: bold'>compilation.Emit</span>(stream); if (<span style='color: blue; font-weight: bold'>compileResult.Success</span> == true) { Assembly compiledAssembly = Assembly.Load(stream.GetBuffer()); Type test = compiledAssembly.GetType("Program"); } else { foreach (var diag in compileResult.Diagnostics) { Console.WriteLine(diag.Info); } } } </pre> <br /> 역시 그렇게 어려운 작업은 아니죠? ^^ (이런 경우, 컴파일 결과만 놓고봐서는 차라리 <a target='tab' href='http://www.sysnet.pe.kr/2/0/1153'>스크립트 엔진</a>을 사용하는 것이 더 간단할 수 있습니다.)<br /> <br /> (아직 CTP 버전이라서 그런지 모르겠지만) 다소 이상한 점이 있다면 EmitResult.Success == false인 경우, 위와 같이 코드에서는 EmitResult.Diagnostics를 이용하여 오류 원인을 알아낼 수 있었던 반면, Visual Studio Debug 창에서는 알 수 없었습니다.<br /> <br /> 굳이 Debug 창을 통해서 알아내려면 ... ^^; 다음과 같은 다소 복잡한 하위 탐색을 이용해서만 가능했습니다.<br /> <br /> [그림: System.Linq를 참조하지 않았을 때의 오류 메시지 열람]<br /> <img onclick='toggle_img(this)' class='imgView' alt='roslyn_binding_1.png' src='/SysWebRes/bbs/roslyn_binding_1.png' /><br /> <br /> <a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=636&boardid=331301885'>첨부된 파일은 위의 코드를 포함한 예제 프로젝트</a>입니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9180
(왼쪽의 숫자를 입력해야 합니다.)