<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>HeeJae's Blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/heejaechang/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/heejaechang/feed.xml</link>
    <description>twitter : sinaweChang</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:09:13 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>로즐린 Scripting vs PowerShell</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/scripting-vs-powershell</link>
      <pubDate>Mon, 31 Oct 2011 16:16:32 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/31/scripting-vs-powershell/</guid>
      <description><![CDATA[안녕하세요. 이번 post는 어떤 분께서 PowerShell과 Roslyn scripting 을 비교해 놓으셨길래 그에 대한 제의견입니다.  일단 PowerShell이 할수 있는걸...]]></description>
      <content:encoded><![CDATA[<p>안녕하세요. 이번 post는 어떤 분께서 <a href="https://technet.microsoft.com/en-us/scriptcenter/dd742419" target="_blank">PowerShell</a>과 <a href="https://msdn.microsoft.com/en-us/hh500769#Toc306015668" target="_blank">Roslyn scripting</a> 을 비교해 놓으셨길래 그에 대한 제의견입니다.</p>  <p>일단 PowerShell이 할수 있는걸 Roslyn scripting도 다 할수 있습니다. 단지 PowerShell이 간단하게 할수 있는걸 Roslyn으로 하려면 훨씬 복잡하고 많은 코딩이 필요합니다. 이유는 PowerShell은 scripting engine 뿐만 아니라, 여러 PowerShell이 지원 하는 시나리오를 쉽게 할수 있는 많은 툴, API를 제공하는 반면, roslyn scripting은 딱 engine만 제공하기 때문이죠. </p>  <p>이걸 이해를 돕기 위해 소방차와 비교해서 설명하자면, PowerShell은 완전히 완성된 소방차라 할수 있죠. 그 소방차만 있으면 불끄는데 필요한 모든것이 갖추어져 있습니다. 그에 반해, Roslyn scripting은 소방차에 필요한 각각의 파트를 제공해 줍니다. 엔진, 사이렌, 물저장소, 펌프 등등. 이걸 잘 조합하면 소방차가 하는 역할과 같은 일을 다 할순 있겠지만, 소방차 만큼 간단하게 같은 일을 처리 할순 없겠죠.</p>  <p>이걸 또 다르게 비교하자면, Roslyn scripting은 각각의 파트고 PowerShell은 그걸 조합해서 말들수 있는 하나의 결과물이라 하겠습니다.</p>  <p>여기서 제가 말하려고 하는 요점은 Roslyn의 여러 부분중 Scripting API는 또 다른 PowerShell을 제공해 주려는것이 아니라, 자신만의 Shell 프로그램을 만들고 싶어 하는 사람들이 좀 더 쉽게 그런 프로그램을 만들수 있도록 그 엔진을 제공해 주는것입니다. 스포츠카와 버스가 둘 다 A에서 B로 이동이 가능하다고 목적이 같다고 할 순 없듯이, PowerShell과 Roslyn Scripting은 그 타켓을 달리 합니다. Roslyn Scripting은 PowerShell을 만드는 사람이 타켓이라면, PowerShell은 IT professional이나 end user가 target이라 할수 있겠죠.</p>  <p>Roslyn Scripting API에 대해 오해가 좀 있는거 같아 올렸습니다.</p>  <p>수고 </p>  <p>- 희제</p>  <p>…</p>  <p>PS: 여기서 또 하나, <a href="https://blogs.msdn.com/b/heejaechang/archive/2011/10/23/the-roslyn-project.aspx" target="_blank">Roslyn 프로젝트는</a> 총 4개의 파트 (Compiler, Scripting, Workspace, Service)로 되어 있고, Scripting 파트는 그 중 하나 입니다. 또 그 Scripting이 2가지 역할 (Scripting API, Interactive Environment) 로 나눠지죠. 여기서 말하는 Scripting API는 2개의 역할중 하나를 말하는겁니다. 한국 블러그를 보니까 너무 scripting에만 많은 포커스가 가 있던데, 나머지 파트 또한 한번 봐 주시면 감사하겠습니다. </p>  <p>PS: 질문이 있으시면, <a href="https://social.msdn.microsoft.com/forums/en-us/roslyn" target="_blank">여기를</a> 이용하시던지, 아니면 제 블로그에 올려주시면 제가 답변 드리도록 하겠습니다.</p>]]></content:encoded>
    </item>
    <item>
      <title>한국 Roslyn 관련 블로그 포스팅 몇개 소개 합니다.</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/roslyn</link>
      <pubDate>Mon, 31 Oct 2011 01:14:40 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/31/roslyn/</guid>
      <description><![CDATA[안녕하세요. 웹 서치 하다 보니까 한국 어느분께서 이미 Roslyn에 대한 기본적인 blog 포스팅을 몇개 하셨길래, 소개차 올립니다.  ….  Roslyn 맛보기 (1) - C#...]]></description>
      <content:encoded><![CDATA[<p>안녕하세요. 웹 서치 하다 보니까 한국 어느분께서 이미 Roslyn에 대한 기본적인 blog 포스팅을 몇개 하셨길래, 소개차 올립니다.</p>  <p>….</p>  <p>Roslyn 맛보기 (1) - <a href="https://www.sysnet.pe.kr/2/0/1153">C# 소스 코드를 스크립트 처럼 다루는 방법</a>    <br />Roslyn 맛보기 (2) - <a href="https://www.sysnet.pe.kr/2/0/1154">C# Interactive (1)</a>    <br /> Roslyn 맛보기 (3) - <a href="https://www.sysnet.pe.kr/2/0/1155">C# Interactive (2)</a>    <br /> Roslyn 맛보기 (4) - <a href="https://www.sysnet.pe.kr/2/0/1156">Roslyn Services APIs를 이용한 Code Issue 및 Code Action 기능 소개</a>    <br />Roslyn 맛보기 (5) - <a href="https://www.sysnet.pe.kr/2/0/1157">Syntax Analysis (Roslyn Syntax API)</a>    <br />Roslyn 맛보기 (6) - <a href="https://www.sysnet.pe.kr/2/0/1158">Roslyn Symbol / Binding API</a>    <br />Roslyn 맛보기 (7) - <a href="https://blog.naver.com/techshare/100141756102" target="_blank">SyntaxTree 조작</a></p>  <p>….</p>  <p>앞으로 Roslyn에 대한 한국 포스팅 또 찾으면 더 올리도록 하겠습니다. </p>  <p>그럼 수고.</p>  <p>- 희제.</p>]]></content:encoded>
    </item>
    <item>
      <title>The Roslyn Project 번역판</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/the-roslyn-project</link>
      <pubDate>Sun, 23 Oct 2011 20:47:09 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/23/the-roslyn-project/</guid>
      <description><![CDATA[* 이건 제 개인적인 번역본 입니다. 공식적인 번역본이 만약 나오면 지우겠습니다.  Exposing the C# and VB compiler’s code analysis...]]></description>
      <content:encoded><![CDATA[<h3 align="right"><font size="2" face="Arial">* 이건 제 개인적인 번역본 입니다. 공식적인 번역본이 만약 나오면 지우겠습니다.</font></h3>  <h3>Exposing the C# and VB compiler’s code analysis</h3>  <p><strong>October 2011</strong></p>  <p>Karen Ng, Principal Lead Program Manager, Microsoft Corporation    <br />Matt Warren, Principal Architect, Microsoft Corporation     <br />Peter Golde, Partner Architect, Microsoft Corporation     <br />Anders Hejlsberg, Technical Fellow, Microsoft Corporation</p>  <p><strong>Contents</strong></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015663">1 Introduction</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015664">2 Exposing the Core APIs</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015665">2.1 Key Concepts</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015666">2.2 API Layers</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015667">2.2.1 Compiler APIs</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015668">2.2.2 Scripting APIs</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015669">2.2.3 Workspace APIs</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015670">2.2.4 Services APIs</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015671">3 Working with Syntax</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015672">3.1 Key Concepts</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015673">3.1.1 Syntax Trees</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015674">3.1.2 Syntax Nodes</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015675">3.1.3 Syntax Tokens</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015676">3.1.4 Syntax Trivia</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015677">3.1.5 Spans</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015678">3.1.6 Kinds</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015679">3.1.7 Errors</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015680">3.2 Obtaining a Syntax Tree</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015681">3.3 Manipulating Syntax Nodes</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015682">3.3.1 Creating a node</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015683">3.3.2 Modifying an existing node</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015684">3.3.3 Replacing a node</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015685">3.3.4 Creating a Syntax Tree</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015686">4 Working with Semantics</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015687">4.1 Key Concepts</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015688">4.1.1 Compilation</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015689">4.1.2 Symbols</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015690">4.1.3 Semantic Model</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015691">4.2 Obtaining a Compilation</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015692">4.3 Obtaining Symbols</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015693">4.4 Asking Semantic Questions</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015694">4.5 Working with Fault Tolerance</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015695">4.6 Using the Control and Data Flow Analysis APIs</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015696">5 Working with a Workspace</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015697">5.1 Key Concepts</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015698">5.1.1 Workspace</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015699">5.1.2 Solutions, Projects, Document</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015700">5.2 Obtaining a Workspace</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015701">5.3 Working with a Solution</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015702">5.4 Updating a Workspace</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015703">6 Working with Services</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015704">6.1 Key Concepts</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015705">6.1.1 Managed Extensibility Framework</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015706">6.2 Creating an IDE extension</a></p>  <p><a href="https://msdn.microsoft.com/en-us/#Toc306015707">7 Summary</a></p>  <h4>1 Introduction</h4>  <p>전통적으로, 컴파일러는 블랙박스였습니다. 소스코드가 한쪽으로 들어가고, 중간에서 마법이 일어나, 마지막에 어셈블리가 되는거였습니다. 컴파일러가 그 마법을 수행하는 동안, 소스코드에 대한 많은 유용한 정보들을 알아내지만, 그 정보들은 바깥에서는 볼수 없었죠. 그리고 그 정보들은 마지막에 모두 버려졌습니다.</p>  <p>수세기동안, 사람들은 이런 방식만으로도 충분히 만족했지만, 점차 IDE가 대세로 자리 잡아 감에 따라, intelliSense, refactoring, rename 등등 컴파일러의 저 버려지는 정보들을 원하는 기능들이 많아져 갔습니다. 로잘린은 바로 이 부분을 채워주기 위한 목적으로 만들어진 프로젝트입니다. 블랙박스를 오픈하여 여러 스마트한 툴들과 사용자들에게 컴파일러가 가지고 있는 코드에 대한 정보를 제공해 주는것이죠. 로즐린을 통해 컴파일러가 서비스가 되는것입니다.</p>  <p>이 로즐린 프로젝트를 통해 스마트한 코드 툴 시장에 들어오는 장벽을 낮춰, 메타 프로그램밍, code generation and transformation, 인터렉티브한 C#, VB 코드, DSL에 C#/VB 삽입등과 같은 여러 분야의 시너지 효과를 기대해 봅니다.</p>  <p>The Microsoft “Roslyn” CTP 은 코드 생성, 분석, 리펙토링, 스크립팅등을 위한 새로운 랭귀지 오브젝트 모델을 제시합니다. 이 문서는 로즐린 프로젝트에 대한 conceptual overview 입니다. 보다 자세한 내용은 원 <a href="https://msdn.microsoft.com/en-us/roslyn" target="_blank">Roslyn</a> 페이지에서 찾아 보실수 있습니다.</p>  <h4>2 Exposing the Core APIs</h4>  <p>로즐린의 Core API는 C#과 VB 컴파일러의 코드 분석 정보를 전통적인 컴파일러 파이프 라인과 비슷한 layer의 API를 통해 제공합니다.</p>  <p><img title="" alt="" src="https://i.msdn.microsoft.com/hh500769.1(en-us,MSDN.10).jpg" /></p>  <p>위의 그림에서 각각의 파이프라인 단계 하나 하나가 콤퍼넌트가 되어, 첫 콤퍼넌트는 소스를 파싱해서 syntax tree를 주고, 그 다음것은 파싱된 소스와 reference들을 통해 심볼을 제공하고, 세번째 바인더는 소스 코드에서 심볼을 찾아주는 역할을 하고, 마지막 컴퍼넌트는 그렇게 모은 정보를 assembly로 추출하는 기능을 제공합니다.</p>  <p><img title="" alt="" src="https://i.msdn.microsoft.com/hh500769.2(en-us,MSDN.10).jpg" /></p>  <p>위의 각각의 역할에 맞게, 각 컴퍼넌트마다 object model이 제공되어 집니다. 이 OB를 통해 각 단계의 정보에 접근하게 되는것이죠. 파싱 단계는 syntax tree를 제공하고, 선언 단계는 체계적인 심볼 테이블을 제공하며, 바인딩 단계는 컴파일러의 semantic 분석 정보를 제공하고, 마지막 단계는 IL을 출력하는 방법을 제공합니다.</p>  <p><img title="" alt="" src="https://i.msdn.microsoft.com/hh500769.3(en-us,MSDN.10).jpg" /></p>  <p>이 전체 파이프라인과 컴퍼넌트들을 하나로 쭉 묶어서 처리하면 컴파일러가 되는것입니다.    <br /></p>  <p>로즐린에서 제공하는 API들이 다른 third party가 툴을 만들기에 충분한 정보를 공개하는것을 보증하게 위해, Roslyn IDE 서비스들은 로즐린에서 제공하는 API만을 가지고 만들어지게 됩니다. 예를 들어 outlining이나 formatting은 syntax tree에서 제공하는 API만을 가지고 만들어지고, navigate to 기능은 심볼 API만을 가지고 만드는 식으로 말입니다.</p>  <h5><em>2.1 Key Concepts</em></h5>  <p>이 섹션에서는 로즐린 프로젝트의 각 레이어의 키 콘셉트들을 알아보도록 하겠습니다.</p>  <h5><em>2.2 API Layers</em></h5>  <p>로즐린 프로젝트는 아래의 API들을 공개합니다 – the Compiler APIs, the Scripting APIs, the Workspace APIs, and the Services APIs.</p>  <p><img title="" alt="" src="https://i.msdn.microsoft.com/hh500769.41(en-us,MSDN.10).jpg" /></p>  <p><strong>2.2.1 Compiler APIs</strong></p>  <p>이 레이어는 언어의 문법적인 부분과 semantic 부분을 포함해 전통적인 컴파일러가 처리하는 모든 정보들을 제공합니다. 예를 들어, reference라던지, 컴파일 옵션이라든지, 소스 파일들이라든지, C#/VB 파일들을 컴파일하기 위해 필요한 모든 정보를 제공합니다. 이 레이어는 C#과 VB가 비슷하지만 다른 API를 사용하게 됩니다. 이 레이어는 Visual Studio에 대한 의존성이 없습니다.</p>  <p><strong>2.2.2 Scripting APIs</strong></p>  <p>이 레이어는 C#이나 VB 코드를 실행하는 환경을 제공합니다. 이 레이어에는 scripting engine을 가지고 있어서 그것을 통해 C#/VB expression과 statement들을 실행할수 있게 해줍니다. 이 레이어 역시 VS에 대한 의존성이 없습니다.</p>  <p><em>Note:</em> 이 문서는 Scripting API를 다루지 않습니다. 보다 자세한 정보를 원하시면, “Introduction to Implementing Host Commands in Script Code”를 참조 하시기 바랍니다.</p>  <p><strong>2.2.3 Workspace APIs</strong></p>  <p>이 레이어는 손쉽게 솔루션 전체에 대한 코드 분석과 refactoring이 가능하게 해 줍니다. 여기서 제공하는 object model을 통해 솔루션에 있는 여러 project들과 그에 관계된 정보들을 간단하게 이용하실수 있습니다. 또한 workspace object model을 통해 수작업 없이 compiler object model을 가져올수 있습니다.</p>  <p>이 레이어는 VS 처럼 특정 host하에서 코드 분석이나 refactoring 을 하기 위해 보통 사용되어 지지만, VS에 대한 의존성은 없습니다.</p>  <p><strong>2.2.4 Services APIs</strong></p>  <p>이 레이어는 인텔리센스, 리펙토링, formatting과 같은 여러 VS IDE 기능들을 서비스로 제공합니다. 이 서비스들을 통해 IDE 기능들을 사용할수도 혹은 경우에 따라 확장할수도 있습니다.</p>  <p>이 레이어는 VS Text Editor와 Visual Studio software development kit (SDK)에 의존성이 있습니다.</p>  <h4>3 Working with Syntax</h4>  <p>syntax tree는 로즐린 컴파일러 API의 가장 밑바닥 API입니다. 이 tree는 소스 코드의 lexical/syntactic 정보를 제공합니다. 이를 통해 중요한 2가지 정보를 제공하게 되는데 첫번째가 툴들이 사용자의 소스코드의 문법적 정보를 접근할수 있게 해 주고, 두번째가 tree 조작을 통해 직접적인 소스 코드 text에 대한 조작없이 코드를 재구성할수 있게 해 줍니다.</p>  <h5><em>3.1 Key Concepts</em></h5>  <p>이 섹션은 Syntax API의 주요 콘셉들을 알아볼것입니다.</p>  <p><strong>3.1.1 Syntax Trees</strong></p>  <p>Syntax tree는 컴파일, 코드 분석, 바인딩, 리펙토링, IDE 기능, 코드 생성들에 사용되는 가장 기본적이고 중요한 정보입니다. syntax tree 없이는 다른 어떤 API도 작동하지 않습니다.</p>  <p>신텍스 트리는 3가지 중요한 특징이 있는데, 첫번째는 이 syntax tree는 source code에 존재 했던 모든 정보를 포함하고 있다는 것입니다. 스페이스, 코멘드, preprocessor 어떤것 하나도 없어지지 않습니다. 예를 들어, 문자열도 정확히 소스에 쓰여진 그대로 보존됩니다. 이 첫번째 특징이 2번째 특징도 가능하게 해줍니다.</p>  <p>두번째 특징은 syntax tree는 완전히 round-trippable 하다는 것입니다. 무슨 말이냐면, tree로 부터 소스를 추출 할 경우, 그 소스는 tree가 만들어진 소스와 완전히 동일한 소스를 재 구성합니다. 이 특징을 이용하면, 단순히 tree를 조작함으로써, 소스 코드를 조작하는것과 동일한 효과를 볼수 있습니다.</p>  <p>세번째 특징은 syntax tree가 immutable하고 thread-safe하다는 것입니다. 무슨 뜻이나면 일단 tree가 만들어진 이후에는 절대 변하지 않는다는 것입니다. 쉽게 말해, 현 소스에 대한 snapshot이라고 생각하시면 됩니다. 따라서 일단 만들어진 tree는 여러 thread에서 아무런 lock 없이 사용하실수 있습니다. 또한 이런 tree를 조작하기 위한 여러가지 factory 함수들이 제공되어 집니다. </p>  <p>syntax tree는 말 그래로 중간 노드가 child 구조를 가지고 있는 tree 데이타 구조입니다. syntax tree는 노드, 토큰, 트리비아로 구성됩니다<em>.</em></p>  <p><strong>3.1.2 Syntax Nodes</strong>&#160; </p>  <p>Syntax node는 syntax tree의 가장 기초적인 구성 요소입니다. 이 노드들은 declarations, statements, clauses, and expressions 등과 같은 문법적인 구문들을 나타냅니다. 각각의 노드들은 각자의 문법 구문에 따라 종류가 나눠지며, 모든 종류의 노드는 SyntaxNode로부터 상속된 타입입니다. 하지만, 사용자들이 특정 노드를 다시 상속함으로써 노드를 확장할수는 없습니다.</p>  <p>모든 syntax node들은 tree의 끝자리 노드가 아닙니다, 무슨 말이냐면, ��든 노드는 자식 노드나 토큰을 가지고 있게 됩니다. 또 모든 자식 노드들은 부모 노드에 대한 포인터를 가지고 있습니다. 루트 노드의 경우는 null을 부모 노드로 가지게 됩니다. syntax tree 와 node는 immutable 이기 때문에, 이 부모/자식간의 관계는 한번 만들어진 이후엔 바뀌지 않습니다.</p>  <p>각각의 노드들은 ChildNodes, DescendentNodes, DescendentTokens 와 같은 함수들을 통해 노드 아래에 있는 정보들을 여러 형태로 가져오는 방법을 지원합니다. 예를 들어, ChildNodes는 부모 노드 바로 밑에 있는 syntax node 만을 돌려주고, DescendentNodes는 부모 노드 아래에 있는 모든 노드들을 돌려주는 식으로 말입니다.</p>  <p>더불어, 각각의 세세한 노드 타입들은 기본적으로 다른 노드 타입들과 공유하는 정보 뿐만 아니라 그 타입만 존재하는 정보들을 제공합니다. 예를 들어 BinaryExpressionSyntax 노드는 Left, OperatorToken, Right과 같은 정보를 제공합니다. 때때로 어떤 노드들은 이렇게 항상 존재하는 정보 뿐만 아니라, 선택적으로 제공되는 정보도 제공합니다. 보통 그런 정보들은 Opt이라는 접미사를 가집니다. 예를 들어, IfStatementSyntax는 ElseClauseOpt 라는 정보를 제공합니다. 선택적인 정보의 경우, 정보가 제공되지 않을 경우 null을 값으로 가지게 됩니다. 선택적 정보가 아닌 경우에는 절대 null 값을 가지지 않습니다.</p>  <p><strong>3.1.3 Syntax Tokens</strong></p>  <p>Syntax token은 keywords, identifiers, literals, and punctuation과 같은 문법적 구문의 가장 끝머리를 나타냅니다. 따라서 토큰은 절대 다른 토큰이나 노드의 부모가 되지 않습니다.</p>  <p>로즐린에서 syntax token은 성능상의 이유로 CLR의 value type입니다. 따라서, 노드와는 다르게 토큰은 오직 한가지 종류만이 존재합니다. 대신 토큰은 토큰 종류에 따라 다른 정보를 제공하는 많은 종류의 property를 제공하게 됩니다. 예를 들어, 정수 숫자를 나타내는 integer literal 토큰의 경우 일반적인 소스 텍스트의 위치 뿐만 아니라, Value property를 통해 그 텍스트가 어떤 정수 숫자로 파서에 의해 인식되었는지도 알려줍니다. 물론 Value property가 다른 종류의 토큰에서도 같이 사용될수 있으므로 type은 안타깝게도 object입니다. 다른 예로, ValueText의 경우는 Value와 같은 정보를 항상 string으로 보여줍니다. 따라서 이것도 그냥 코드에 나와 있는 값의 string 표현이 아니라, 파서가 인식한 값의 string 표현으로 보여줍니다.&#160; </p>  <p><strong>3.1.4 Syntax Trivia</strong></p>  <p>Syntax trivia는 코드상의 whitespace, comments, and preprocessor directives과 같이 프로그램상에서는 별 의미가 없지만, 툴에게는 필요할수도 있는 정보들을 포함합니다. 이 trivia는 실제 언어 문법에 포함되지 않고, 아무 토큰 사이라도 있을수 있기 때문에, syntax tree에 자식노드로는 포함되어 있지 않습니다. 하지만, 툴들이나 syntax tree의 full fidelity를 제공하기 위해 syntax tree 안에 정보가 저장은 되어 있습니다.</p>  <p>이 정보들은 각 토큰의 LeadingTrivia 와 TrailingTrivia를 통해 접근할수 있습니다. 소스를 parse 하는 동안 일련의 trivia들이 옆에 있는 token들과 맵핑되어 집니다. 일반적인 규칙은 같은 줄에 있는 토큰 뒤에 있는 trivia들은 그 앞에 있는 토큰에게 맵핑됩니다. 만약 2개의 토큰이 2줄에 걸쳐 있으면, 나머지 trivia들은 다음 토큰에 맵핑됩니다. 소스의 첫번째 토큰은 그 앞에 있는 모든 trivia 들과 맵핑되고 소스의 가장 마지막 토큰은 그 뒤에 있는 모든 trivia와 맵핑됩니다.&#160; </p>  <p>노드와 토큰과는 다르게 trivia는 부모 property가 없습니다. 이유는 trivia는 tree에 자식으로 포함되지 않고, 연관된 토큰과 맵핑만 되기 때문입니다. 따라서 trivia는 token이라는 property를 가지고 있습니다.</p>  <p>토큰과 마찬가지로 trivia는 성능상의 이유로 value type입니다. 따라서 하나의 SyntaxTrivia 타입으로 여러가지 다른 종류의 trivia를 나타냅니다.</p>  <p><strong>3.1.5 Spans</strong></p>  <p>모든 노드와 토큰과 트리비아는 소스 코드상에서의 자신의 위치와 크기를 기억하고 있습니다. 소스상의 텍스트의 위치는 0 베이스의 유니코드 32비트 정수 인덱스로 표현되는데, 이를 위해 TextSpan이라는 타입을 사용합니다. 이 타입을 이용해 텍스트의 위치와 크기를 알수 있습니다. 만약 TextSpan이 0 크기를 가지고 있다면, 이건 2글자 사이를 나타내는것입니다.</p>  <p>모든 노드와 토큰은 Span과 FullSpan을 가지게 되는데, Span은 가장 처음 토큰의 시작 위치부터 가장 마지막 토큰의 끝나는 위치까지를 TextSpan으로 가지게 되고, FullSpan은 가장 첫 토큰의 맨 앞 트리비아의 첫 위치와 맨 마지막 토큰의 맨 마지막 트리비아의 끝 위치를 TextSpan으로 갖게 됩니다.</p>  <p>예를 들어 </p>  <pre><code>if (x &gt; 3)
{
<font style="background-color: rgb(255,255,0)">    // this is bad
    <u>throw new Exception(“Not right.”);</u>  // better exception?</font>
}</code></pre>

<p>블락안에 있는 statement 노드의 span은 밑줄로 표시되어 있고, full span은 노란색으로 표시 되어 있습니다.</p>

<p><strong>3.1.6 Kinds</strong></p>

<p>모든 노드, 토큰, 트리비아는 SyntaxKind 타입의 Kind property가 있습니다. 이를 통해 각각의 노드, 토큰, 트리비아가 어떤 문법 구문을 나타내는지 알수 있습니다. 각각의 언어마다 그 언어의 모든 노드, 토큰, 트리비아를 나타내는 SyntaxKind 타입이 있습니다.</p>

<p>이 Kind Property를 통해서 동일한 node 타입이 다른 구문에 쓰이는걸 구별하실수 있습니다. 토큰과 트리비아의 경우는 이 kind 프로펄티가 여러 다른 종류의 토큰과 트리비아를 구별하는 유일한 방법입니다.</p>

<p>예를 들어, BinaryExpressionSyntax 노드가 AddExpression인지 아니면 SubstractExpression, MultiplyExpression인지 kind property를 통해서 알수 있습니다.</p>

<p><strong>3.1.7 Errors</strong></p>

<p>위에서 말한 syntax tree의 round-trippable한 특성은 코드에 에러가 있는 경우도 유효합니다. 만약 파서가 소스 코드를 분석하는 동안, 문법에 맞지 않는 구문을 만난다면, 다음 2개중 하나를 하게 됩니다.</p>

<p>첫번째, 만약 파서가 어떤 특정한 종류의 토큰을 기다리고 있었지만, 찾지 못했다면, 파서가 빠진 부분을 missing token으로 대체하고 계속 파싱을 할것입니다. missing token은 IsMissing property를 통해 확인 할수 있으며, 0 길이의 Span을 가지게 됩니다.</p>

<p>두번째로, 만약 파서가 현 위치의 소스코드를 전혀 이해 할수 없다면, 파서가 이해 할수 있는 부분을 만날 때까지 이해할수 없는 부분의 토큰들을 SkippedTokens 이라는 종류의 trivia로 만들어 trivia 룰에 따라 토큰에 맵핑하게 됩니다.</p>

<h5><em>3.2 Obtaining a Syntax Tree</em></h5>

<p>Roslyn.Compilers와 Roslyn.Compilers.Common 안에는 공용 Syntax API가 있고, Roslyn.Compilers.CSharp와 Roslyn.Compilers.VisualBasic 안에는 각 언어 특유의 Syntax API가 있습니다. 각각에 들어 있는 API들은 각각 다음의 namespace를 이용하여 사용하실수 있습니다.</p>

<pre><code>using Roslyn.Compilers;

using Roslyn.Compilers.Common;

using Roslyn.Compilers.CSharp;

using Roslyn.Compilers.VisualBasic;</code></pre>

<p>가장 쉽게 소스 코드에 대한 syntax tree를 얻는 방법은 SyntaxTree.ParseCompliationUnit 함수를 사용하는 방법입니다. </p>

<p>예를 들어 아래와 같이 하면 됩니다.</p>

<pre><code>SyntaxTree tree = SyntaxTree.ParseCompilationUnit(
                @&quot;using System;
 
                namespace HelloWorld
                {
                    class Program
                    {
                        static void Main(string[] args)
                        {
                            Console.WriteLine(&quot;&quot;Hello, World!&quot;&quot;);
                        }
                    }
                }&quot;);</code></pre>

<p>syntax tree 뿐만 아니라 syntax node도 유사한 방법으로 얻으실수 있습니다. Syntax 타입은 다양한 parsing 함수를 제공합니다. 이것을 통해, 전체 프로그램이 아닌 부분 이를테면 statement, expression 심지어 comment만 따로 parsing 하실수 있습니다.</p>

<pre><code>ExpressionSyntax myExpression = Syntax.ParseExpression(&quot;1+1&quot;);

StatementSyntax myStatement = Syntax.ParseStatement(&quot;for (int i = 0; i &lt; length; i++) { }&quot;);</code></pre>

<p>위의 예의 경우, myExpression의 타입은 BinaryExpressionSyntax 이고 myStatement의 타입은 ForStatementSyntax 입니다.</p>

<h5><em>3.3 Manipulating Syntax Nodes</em></h5>

<p>Roslyn은 기존 tree를 변형 한다던지 새로운 syntax node를 삽입함으로써 좀 더 쉽게 코드를 조작할수 있는 방법을 제공합니다.</p>

<p><strong>3.3.1 Creating a node</strong></p>

<p>Syntax class 안에 있는 factory 함수를 이용하여 새로운 syntax node를 생성하실수 있습니다. 각각의 syntax node, token, trivia 마다 각각을 위한 factory 함수가 Syntax class안에 마련되어 있습니다.</p>

<p>예를 들어, “Roslyn.Compilers” 라는 namespace declaration node를 만든다고 하면, 일단 namepace keyword token과 name node가 필요한데, namespace keyword token은 Syntax.Token 함수를 통해 아래 처럼 만들수 있고,</p>

<pre><code>SyntaxToken namespaceToken = Syntax.Token(SyntaxKind.NamespaceKeyword);</code></pre>

<p>“Roslyn.Compilers” 라는 name node는 아래 예처럼 만드실수 있습니다.</p>

<pre><code>QualifiedNameSyntax namespaceName = Syntax.QualifiedName(
                           left: Syntax.IdentifierName(&quot;Roslyn&quot;), 
                           right: Syntax.IdentifierName(&quot;Compilers&quot;));</code></pre>

<p>위의 방식이 너무 복잡하다만, 아래와 같이 ParserName 방법을 쓰실수도 있습니다.</p>

<pre><code>NameSyntax namespaceName = Syntax.ParseName(&quot;Roslyn.Compilers&quot;);</code></pre>

<p>마지막으로 위에서 만든 token과 node를 이용해서 아래와 같이 namespace declaration node를 만드실수 있습니다.</p>

<pre><code>NamespaceDeclarationSyntax myNamespace = Syntax.NamespaceDeclaration(
                           namespaceKeyword: namespaceToken, 
                           name: namespaceName);</code></pre>

<p>만약 위의 namespace declaration node를 text로 바꾼다면 아래와 같은 text를 얻게 되실겁니다.</p>

<pre><code>namespaceRoslyn.Compilers{}</code></pre>

<p>안타깝게도, 위에 처럼 만들어진 text는 글자 사이에 빈공간이 없습니다. 만약 위의 text를 다시 parse 하실려고 하면, 에러가 날것입니다. 이런 경과를 원하지 않으신다면 node와 token을 만드실때 trivia를 직접 지정해 주셔야 합니다.</p>

<p>namespace keyword token 뒤에 아래처럼 Space trivia를 지정해 줌으로써 text를 생성시 namespace 뒤에 space가 오게 하실수 있습니다.</p>

<pre><code>SyntaxToken namespaceToken = Syntax.Token(
                             SyntaxKind.NamespaceKeyword, 
                             Syntax.Space);</code></pre>

<p>text로 변환 했을시 결과물입니다.</p>

<pre><code>namespace Roslyn.Compilers{}</code></pre>

<p>위처럼 자신이 직접 trivia를 지정하는걸 원치 않으실 경우, node를 built-in style로 아래처럼 format 하실수 있습니다.</p>

<pre><code>NamespaceDeclarationSyntax formattedNamespace = myNamespace.Format();</code></pre>

<p>결과는 아래와 같습니다.</p>

<pre><code>namespace Roslyn.Compilers
{
}</code></pre>

<p><strong>3.3.2 Modifying an existing node</strong></p>

<p>만약 어떤 분석후 기존 소스 코드를 변환 시키고 싶으실 경우, 일반적으로 완전히 새로운 node를 만들기 보단 기존에 있던 node를 변형 시키고 싶으실 것입니다. 이때 이 변형된 node를 기존 node의 Update 함수를 이용하여 얻으실수 있습니다.</p>

<p>예를 들어, Section 3.2에 잇는 HelloWord namespace 를 Roslyn.Compilers namespace로 update 하고 싶으실 경우, 아래와 같이 예전 namespace node를 찾으신 후</p>

<pre><code>NamespaceDeclarationSyntax oldNamespace = tree.Root.DescendentNodes()
                          .OfType&lt;NamespaceDeclarationSyntax&gt;()
                          .First();</code></pre>

<p>아래 처럼 Update 함수를 이용하여 새 namespace node를 얻으실수 있습니다.</p>

<pre><code>NamespaceDeclarationSyntax newNamespace = 
			oldNamespace.Update(oldNamespace.NamespaceKeyword, 
                                       namespaceName, 
                                       oldNamespace.OpenBraceToken, 
                                       oldNamespace.Externs, 
                                       oldNamespace.Usings, 
                                       oldNamespace.Members, 
                                       oldNamespace.CloseBraceToken, 
                                       oldNamespace.SemicolonTokenOpt);</code></pre>

<p>만약, 위의 새로운 namespace node를 text로 변환 하실 경우, 아래와 같은 text를 얻으실수 있습니다.</p>

<pre><code>namespace Roslyn.Compilers {
       class Program
       {
             static void Main(string[] args)
             {
                    Console.WriteLine(&quot;&quot;Hello, World!&quot;&quot;);
             }
       }
}</code></pre>

<p><strong>3.3.3 Replacing a node</strong></p>

<p>tree에서 기존의 node를 다른 node로 완전히 교체하고 싶으시다면, ReplaceNode 함수를 이용하여 아래와 같이 교체 하실수 있습니다.</p>

<pre><code>SyntaxNode newRoot = tree.Root.ReplaceNode(oldNamespace, newNamespace);</code></pre>

<p>만약 newRoot을 text로 변환 하신다면 아래와 같은 코드를 얻으실것입니다.</p>

<pre><code>using System;
 
namespace Roslyn.Compilers
{
     class Program
     {
          static void Main(string[] args)
          {
               Console.WriteLine(&quot;&quot;Hello, World!&quot;&quot;);
	   }
     }
}</code></pre>

<p><strong>3.3.4 Creating a Syntax Tree</strong></p>

<p>위에서처럼 tree의 한 부분을 고치실 경우, 기존 tree를 수정하신다고 생각하실수 있겠으나, 사실 tree가 immutable 하기 때문에 완전히 새로운 tree가 생성되는것입니다. 좀 더 정확히 말하면, 새로운 root node가 만들어 지는 것이죠.</p>

<p>만약 새로운 syntax tree가 필요하다면, 아래와 같이 새로운 tree를 만드실수 있습니다.</p>

<pre><code>SyntaxTree newTree = SyntaxTree.Create(tree.Filename, newRoot, tree.Options);</code></pre>

<h4>4 Working with Semantics</h4>

<p>Syntax tree가 소스의 문법적인 부분에 대한 정보를 제공해 준다면, semantic은 소스의 의미/심볼 그리고 reference들에 대한 정보를 제공해 줍니다.</p>

<p>예를 들어, 많은 같은 이름의 타입, 필드, 함수, 지역 변수가 전체 프로그램네 다른 scope안에서 사용되어 지는데, 이 같은 이름 중 어떤게 어떤것을 가르키는지 알기 위해선 C#/VB 언어 자체의 룰들에 대한 이해를 필요로 하게 되는데, 이때 정보를 제공해 주는것이 semantic API 입니다. </p>

<p>추가로, 프로그램네에서 사용되는 심볼들이 소스에서만 오는게 아니라, reference들에서도 오기 때문에, 소스의 syntax tree 만으론 전체 프로그램을 나타낼수 없게 됩니다. semantic model을 통해 이런 정보들를 제공 받으실수 있습니다.</p>

<h5><em>4.1 Key Concepts</em></h5>

<p>이번 section에서는 Semantic API가 어떤것이며, 어떤 정보를 제공해 주고 어떻게 사용할수 있는지에 대해 알아보겠습니다.</p>

<p><strong>4.1.1 Compilation</strong></p>

<p><em>compilation</em> 은 하나의 C# 혹은 VB 프로그램을 compile 하기 위한 모든 것을 나타내는 concept/type입니다. 따라서, 이 compilation을 이용하여 assembly 레퍼런스, 컴파일 옵션, 소스 파일등에 대한 정보를 가져 올수 있습니다.</p>

<p>이 모든 정보들이 한군데에 모여 있기 때문에, 소스 파일과 연관된 여러 정보들을 제공해 줄수 있습니다. 예를 들어 compilation은 symbol이란 이름으로 그안에 정의되어 있는 모든 type, member, variable들에 대한 정보를 제공해 줄수도 있고, compilation가 제공하는 여러 API를 통해 소스에서 정의되어진 symbol간의 관계나 reference로 부터 import된 심불들 간의 관계에 대한 정보도 제공해 줄수 있습니다.</p>

<p>syntax tree와 유사하게, compilation 또한 immutable입니다. 따라서 일단 compilation이 만들어진 이후에는 수정 되어지지 않습니다. 역시나 syntax tree와 마찬가지로 기존 compilation으로 부터 새 compilation을 생성하실수 있습니다. 예를 들어 새로운 reference를 기존 compilation에 추가 한다던지 새로운 소스 파일 혹은 syntax tree를 추가 하는 방법으로 수정된 새 compilation을 얻으실수 있습니다.</p>

<p><strong>4.1.2 Symbols</strong></p>

<p>심볼은 소스에 정의 되거나 레퍼런스로 부터 import된 어떤 하나의 객체를 나타냅니다. 예를 들어, 모든 namespace, type, method, property, field, event, parameter, local variable들은 각각 하나의 심볼로 나타내어 집니다.</p>

<p>Compilation의 여러 API를 이용해 하나의 컴파일레이션 안에 정의된 symbol들을 찾으실수 있습니다. 예를들어, 타입 심볼을 그의 메타데이타 이름을 통해 찾으실수도 있고, 아니면 트리 형태로 된 전체 심볼 테이블을 직접 돌아다녀서 찾으실수도 있습니다.</p>

<p>심볼은 컴파일러에 의해 알아내어진 여러 정보들을 제공해 줍니다. 각각의 심볼들은 그 종류에 따라 여러 subclass로 나눠지는데, 각각의 subclass들은 그 종류마다 다른 정보들을 제공하게 됩니다. 예를 들어, MethodSymbol의 ReturnType property는 그 method가 return하는 TypeSymbol을 리턴해 줍니다.</p>

<p>또한 심볼은 다른 경로로 들어온 정보들을 (소스, reference) 하나의 형태로 보는것을 가능하게 해 줍니다. 예를 들어 소스에 정의된 함수와 assembly로 부터 import된 함수가 같은 MethodSymbol로 표현 되어 집니다.</p>

<p>심볼은 어떤면으론 System.Reflection API로 대표되는 CLR type system의 콘셉과 비슷하지만, 그것과는 다르게 type에 대한 정보 뿐만 아니라 namespace, local variables, labels 등 더 다양한 부분에 대한 정보 역시 제공합니다. 추가로 심볼은 C#, VB 언어를 나타내는 개념이지 CLR을 나타내는 개념이 아닙니다. 물론 둘 사이에 overlap 되는 부분도 있지만, 충분히 다른 개념을 나타내는 부분도 있습니다. 예를 들어, C#/VB의 iterator method는 Roslyn에서는 하나의 symbol 이지만, 일단 이것이 CLR metadata로 바뀐 이후엔 하나의 타입과 여러개의 method가 됩니다.</p>

<p><strong>4.1.3 Semantic Model</strong></p>

<p><em>semantic model</em> 은 하나의 소스 파일에 있는 모든 semantic 정보를 나타냅니다. 이를 통해 다음과 같은 정보를 알아내실수 있습니다.</p>

<ul>
  <li>소스의 특정 위치에 있는 심볼 </li>

  <li>모든 expression의 타입 </li>

  <li>파일에 있는 모든 warning과 error </li>

  <li>특정 변수가 파일을 특정 부분에 어떻게 사용되어 졌는지 </li>

  <li>소스에는 없는 추측적 질문에 대한 답변 </li>
</ul>

<h5><em>4.2 Obtaining a Compilation</em></h5>

<p>compilation은 syntax tree, assembly reference, compilation option을 이용하여 생성 하실수 있습니다. compilation을 만드는 행위가 disk에 실제 assembly 파일을 만들진 않습니다. compilation은 주어진 정보에 대한 컴파일러 분석의 결과를 나타낼 뿐입니다.</p>

<p>아래와 같이 Create factory 함수를 이용하여 여러가지 방법으로 Compilation을 생성하실수 있습니다.</p>

<pre><code>SyntaxTree tree = SyntaxTree.ParseCompilationUnit(sourceText);
 
AssemblyFileReference mscorlib = new AssemblyFileReference(
                  		      typeof(object).Assembly.Location);

Compilation compilation = Compilation.Create(
                		outputName: &quot;HelloWorld&quot;,
                		syntaxTrees: new [] { tree },
                		references: new MetadataReference[] {
                                          mscorlib });

Compilation compilation = Compilation.Create(&quot;HelloWorld&quot;)
 		  		.AddReferences(mscorlib)
		  		.AddSyntaxTrees(tree);</code></pre>

<p>위의 2가지 방법 중 아래 방법은 임시 compilation을 생성하게 되지만, 그것이 특별히 성능에 영향을 주진 않습니다. 모든 실제 분석 작업은 실제 그 정보가 필요해 질때 까지 연기 되어 집니다.</p>

<h5><em>4.3 Obtaining Symbols</em></h5>

<p>compilation과 symbol에 있는 여러 함수와 property를 이용해서 compilation안에 있는 모든 symbol들을 검색하실수 있습니다. compilation 안에 symbol table은 tree 형태로 저장되어 있고, 그 가장 root은 글로벌 네임스페이스 입니다. 그리고 그 글로벌 네임스페이스는 아래와 같은 방법으로 얻으실수 있습니다.</p>

<pre><code>NamespaceSymbol globalNamespace = compilation.GlobalNamespace;</code></pre>

<p>이 네임스페이스 심볼로 부터, 그 아래있는 자식 네임스페이스 혹은 타입과 같은 모든 멤버들을 얻으실수 있습니다.</p>

<pre><code>foreach (Symbol member in globalNamespace.GetMembers())
{
	// Process member
}</code></pre>

<p>이렇게 모든 멤버를 다 찾는 방식 말고, 특정 이름을 가진 멤버만 찾는 방법도 있습니다. 일반적으로 특정 이름을 가진 멤버만 찾는게, 모든 멤버를 가져와서 원하는 이름을 가진 멤버만 따로 추수리는것 보다 효율적입니다. 아래는 “System.String”이란 이름을 가진 멤버만 찾는 예입니다.</p>

<pre><code>NamespaceSymbol systemNamespace = globalNamespace.GetMembers(&quot;System&quot;)
				       .First() as NamespaceSymbol;

NamedTypeSymbol stringType = systemNamespace.GetMembers(&quot;String&quot;)
				  .First() as NamedTypeSymbol;</code></pre>

<p>또 다른 방법은 CLR metadata에 사용되어지는 매타데이타 이름을 사용하여 심볼을 찾는 방식입니다. 아래는 그 간단한 예입니다.</p>

<pre><code>NamedTypeSymbol stringType = compilation.GetTypeByMetadataName(
				  &quot;System.String&quot;);</code></pre>

<p>타입 심볼로 부턴 field, method, property, event, nested type과 같은 모든 멤버들 정보를 얻으실수 있습니다.</p>

<pre><code>foreach (Symbol member in stringType.GetMembers())
{
    // Process member
}</code></pre>

<p>이밖에도 심볼이 제공하는 다른 property와 함수들을 이용하여 연관된 정보들을 얻으실수 있습니다. 예를 들어 밑의 코드는 string type symbol을 이용하여 string type의 base type과 interface 정보를 알아내는 예입니다.</p>

<pre><code>NamedTypeSymbol baseType = stringType.BaseType;

foreach (NamedTypeSymbol i in stringType.Interfaces) { /* ... */ }</code></pre>

<h5><em>4.4 Asking Semantic Questions</em></h5>

<p>SemanticModel이라는 타입을 이용하여 하나의 소스 파일에 대한 여러 semantic에 관계된 정보 알아내실수 있습니다. 예를 들어 어떤 expression의 결과 타입이라든지, invocation에 사용된 method symbol이라든지. 이런 정보를 SemanticModel를 통해 얻으실수 있습니다.</p>

<p>이 SemanticModel은 compilation으로부터 아래와 같은 방법을 이용하여 얻으실수 있습니다.</p>

<pre><code>SemanticModel semanticModel = compilation.GetSemanticModel(tree);</code></pre>

<p>위에 사용된 tree는 반드시 compilation을 만들때 사용되어진 syntax tree중 하나 이어야 합니다. 이 방법 이외에 Section 5에 나와 있는 IDocument를 이용해서도 SemanticModel을 얻으실수도 있습니다.</p>

<p>일단 SemanticModel을 얻으셨으면, 아래와 같이 여러가지 정보를 Model로 부터 알아내실수 있습니다. 첫번째 예는 method declaration node를 이용하여 method symbol을 찾아내는 방법입니다.</p>

<pre><code>MethodDeclarationSyntax methodDecl = tree.Root
					  .DescendentNodes()
					  .OfType&lt;MethodDeclarationSyntax&gt;()
					  .First();
                                  
Symbol methodSymbol = semanticModel.GetDeclaredSymbol(methodDecl);</code></pre>

<p>또 다른 예는 expression node로부터 그 결과 type을 알아내는 예제 입니다.</p>

<pre><code>ExpressionSyntax expression = methodDecl.DescendentNodes()
			    	  .OfType&lt;ExpressionSyntax&gt;()
			    	  .First();
                  
SemanticInfo info = semanticModel.GetSemanticInfo(expression);

TypeSymbol resultantType = info.Type;</code></pre>

<p>특정 invocation에 컴파일 타임에 bind된 method가 무엇인지도 아래와 같이 알아내실수 있습니다</p>

<pre><code>InvocationExpressionSyntax invocation = expression.DescendentNodes()
				      .OfType&lt;InvocationExpressionSyntax&gt;()
				      .First();
                  
SemanticInfo info = semanticModel.GetSemanticInfo(invocation);

Symbol invokedSymbol = info.Symbol;</code></pre>

<p>여기서 한가지 알아두셔야 할것은 SemanticInfo 타입의 Symbol과 Type property는 서로 다른 의미의 정보라는것입니다. Type property는 주어진 expression의 결과 타입을 나타내고, Symbol property는 expression에 의해 참조된 하나의 심볼을 나타냅니다. 어떤 expression은 Type은 있지만 Symbol은 없을수 있고, 어떤 expression은 둘 다 있을수 있습니다. 예를 들어 invocation expression은 두 정보를 다 가지게 되는데, Symbol은 invoke된 method symbol을 나타내게 되고, Type은 그 method의 return type이 되게 됩니다.</p>

<p>만약, 소스에는 없는 expression의 결과 type을 특정 소스의 위치에서 알아보고 싶다면, BindExpression과 같은 speculatvie 함수를 쓰시면 됩니다.</p>

<pre><code>ExpressionSyntax otherExpression = Syntax.ParseExpression(&quot;X.Y&quot;);
int location = methodDecl.BodyOpt.Span.Start;
                
SemanticInfo info = semanticModel.BindExpression(location,
			  			      otherExpression);
TypeSymbol resultantType = info.Type;</code></pre>

<p>이 외에 BindType과 같은 추측함수를 이용하여 특정 type 이름이 소스의 특정 위치에서 어떤 type으로 bind되는지도 알아보실수 있습니다.</p>

<pre><code>ExpressionSyntax typeExpression = Syntax.ParseTypeName(&quot;System.String&quot;);
int location = methodDecl.BodyOpt.Span.Start;
        
SemanticInfo info = semanticModel.BindType(location, typeExpression);

TypeSymbol resultantType = info.Symbol;</code></pre>

<h5><em>4.5 Working with Fault Tolerance</em></h5>

<p>일반적으로 사용자가 코드중에 사용하는 툴을 만드는 경우, 그 툴이 분석하는 코드에 에러가 없을 확율은 거의 없습니다. 대부분의 경우, 이런 툴들은 여러가지 문법적 에러와 애매모호한 상황을 처리할수 있어야 합니다.</p>

<p>예를 들어, 에러 혹은 여러가지로 해석 될수 있는 상황에서 semantic model은 best guess 결과 타입을 제공해 줌으로써, 이런 상황에서 조차 사용자의 코드를 분석할수 있는 방법을 제공해 줍니다. 이를테면, 어떤 method invocation이 2개 이상의 overload로 bind 가능할 경우, 어떤 하나의 method를 고르지는 못하지만, 만약 둘이 같은 return type을 가지고 있다면, 그 type을 method invocation의 결과 type으로 돌려주는것입니다.</p>

<p>또 다른 예로, 위의 경우 bind 가능한 모든 symbol들을 아래처럼 후보자로 돌려줍니다.</p>

<pre><code>InvocationExpressionSyntax invocation = expression.DescendentNodes()
				.OfType&lt;InvocationExpressionSyntax&gt;()
				.First();
 
SemanticInfo info = semanticModel.GetSemanticInfo(invocation);
Symbol invokedSymbol = info.Symbol;

if (invokedSymbol == null)
{
    ReadOnlyArray&lt;Symbol&gt; candidates = info.CandidateSymbols;
    CandidateReason reason = info.CandidateReason;
    ...
}</code></pre>

<p>위의 CandidateSymbols property는 후보자들의 리스트를 돌려주고, CandidateReason은 왜 하나의 symbol로 bind되지 못했나 이유를 알려줍니다.</p>

<h5><em>4.6 Using the Control and Data Flow Analysis APIs</em></h5>

<p>만약 특정 소스 구역에 어떤 로컬 변수가 사용��어지고, 변수 값이 어떻게 이용되어 졌는지 알고 싶다면, Data Flow Analysis API를 사용하시면 됩니다. 또한 Control Flow Analysis API를 사용하시면, 어떤 statement이 어떻게 콘트롤 flow를 바꾸었는지 알아내실수 있습니다.</p>

<p>control flow 정보는 아래와 같이 semantic model의 AnalyzeRegionControlFlow 함수를 이용해 알아내실수 있습니다.</p>

<pre><code>TextSpan span = methodDecl.BodyOpt.Span;    
             
RegionControlFlowAnalysis controlFlow =
			        semanticModel.AnalyzeRegionControlFlow(span);</code></pre>

<p>구역안에 사용된 return statement를 알아내고 싶으시면 아래와 같이 하시면 됩니다.</p>

<pre><code>var returns = controlFlow.ReturnStatements;</code></pre>

<p>현 구역에서 jump out하게 만드는 statement들은 아래와 같이 알아내실수 있고</p>

<pre><code>var jumpsOut = controlFlow.JumpsOutOfRegion;</code></pre>

<p>현 구역으로 jump in하게 만드는 statement들은 아래와 같이 알아내실수 있습니다.</p>

<pre><code>var jumpsIn = controlFlow.JumpsIntoRegion;</code></pre>

<p>만약 구역안에서 사용된 변수들에 대한 정보를 알고 싶으시다면, semantic model의 AnalyzeRegionDataFlow 함수를 사용하시면 됩니다.</p>

<pre><code>RegionDataFlowAnalysis dataFlow =
			    semanticModel.AnalyzeRegionDataFlow(span);</code></pre>

<p>구역안에서 정의된 로컬 변수는 이렇게 알아내실수 있고</p>

<pre><code>var declared = dataFlow.VariablesDeclared;</code></pre>

<p>구역 밖에서 정의되고 assign된 변수가 구역안 read됐는지는 이렇게 아실수 있고</p>

<pre><code>var usedInside = dataFlow.DataFlowsIn;</code></pre>

<p>구역안에서 assign된 변수가 구역밖에서 read됐는지는 이렇게 아실수 있습니다.</p>

<pre><code>var usedOutside = dataFlow.DataFlowsOut;</code></pre>

<p>이 밖에도 여러 정보를 data/control flow analysis API를 통해 알아내실수 있습니다. 
  <br /></p>

<h4>5 Working with a Workspace</h4>

<p>workspace layer 야 말로 VS 전체 솔루션에 대한 분석과 refactoring을 쉽게 가능하게 해 주는 layer입니다. workspace는 하나의 솔수션의 모든 project와 그와 연관된 정보들을 하나의 object model로 표현해 줍니다. 또한 workspace object model로부터 자연스럽게 compiler layer의 syntax tree, semantic model, compilation과 같은 object model들을 가져오는 방법들을 제공합니다. workspace API를 이용하면 사용자가 직접 코드 소스를 parse하거나, compilation option을 설정하거나, assembly reference들을 설정하거나 할 필요 없이 workspace layer가 알아서 다 처리해 줍니다.</p>

<p>이 workspace는 IDE와 같은 Host 환경이 현재 host에 load된 solution에 맞춰 생성하게 됩니다. 하지만 그렇다고 이게 IDE와 같은 Host에서만 사용할수 있는것은 아닙니다. VS sln파일을 이용하여 workspace를 바로 생성하여 Console에서 사용할수도 있습니다.</p>

<h5><em>5.1 Key Concepts</em></h5>

<p>이 섹션에서는 workspace api에 대한 콘셉과 이를 이용하여 얼마나 쉽게 solution, project, document를 다룰수 있는지 알아볼것입니다.</p>

<p><strong>5.1.1 Workspace</strong></p>

<p>workspace는 host 환경과 타이트하게 연결되어져 있는, 사용자의 host와의 상호작용(typing과 같은)에 따라 끊임없이 변하는 solution, project, document의 live reprentation입니다.</p>

<p>workspace는 밑에 설명되어 있는 그 만의 object model을 통해 현 host의 상태를 제공합니다. host와 사용자의 상호작용이 일어날때 마다, workspace는 그에 상응하는 event를 fire하고, CurrentSolution property를 업데이트합니다. 예를 들어, 사용자가 host의 에디터에 타입을 했다면, workspace는 수정된 document와 기타 정보를 event를 통해 알려주고, 그 정보를 듣고 있는 툴들은 그 change 정보 혹은 current solution을 바탕으로 새로운 코드 분석과 여러가지 작업을 하게 되는것입니다.</p>

<p>그러나, workspace가 꼭 host 환경에서만 사용될수 있는것은 아닙니다. 호스트 환경 없는 stand-alone한 workspace를 만드실수도 있습니다.</p>

<p><strong>5.1.2 Solutions, Projects, Documents</strong></p>

<p>비록 workspace는 사용자와 host간에 상호작용이 있을때 마다 변하지만, solution object model은 그와 상관 없이 독립적으로 이용되어질수 있습니다.</p>

<p><em>solution</em> object model은 다른 layer의 object model과 마찬가지로 immutable 합니다. 단지 이번엔 solution, project, document 들에 대한 정보를 제공합니다. 역시나 다른 object model과 마찬가지로, CurrentSolution property를 통해 얻은 solution은 아무런 lock 없이 여러 thread에서 사용하실수 있고, 기존 solution에 change를 적용함으로써 새로운 solution을 얻을수 있습니다. 나중에 이 새로운 solution을 workspace에 적용함으로써 change를 실제 host에 적용할수도 있습니다.</p>

<p><em>project</em> model은 immutable한 solution model의 project를 나타내는 한 부분입니다. 이 프로젝트 모델은 project가 가지고 있는 소스 코드, parse와 compilation 옵션, assembly와 project-to-project 참조와 같은 모든 연관된 정보를 제공합니다. 이 project model로 부터 이 project 와 대응하는 compiler layer의 compilation object model을 가져 오실수 있습니다.</p>

<p><em>document</em> model 역시 immutable한 solution model의 하나의 부분으로써, 하나의 소스 코드 파일을 나타냅니다. 이를통해 실제 소스 코드, syntax tree 그리고 semantic model를 얻을수 있습니다.</p>

<p>밑에 그림은 workspace layer가 여러 다른 layer와 host와 어떤 식으로 연결되어 있으며, 소스 코드에 edit이 일어날 경우 어떤식으로 처리가 되는지 간단하게 표현해 놓은것입니다.</p>

<p><img title="" alt="" src="https://i.msdn.microsoft.com/hh500769.6(en-us,MSDN.10).jpg" /></p>

<h5><em>5.2 Obtaining a Workspace</em></h5>

<p>Workspace API는 Roslyn.Service namespace를 using 함으로써 사용하실수 있습니다.</p>

<pre><code>using Roslyn.Services;</code></pre>

<p>일반적으로 workspace는 VS IDE와 같은 host 환경으로 부터 제공되지만, 원하신다면 아래와 같이 host 환경 없이 본인이 직접 만들거나 VS sln을 load하여 사용하실수 있습니다.</p>

<pre><code>IWorkspace workspace = Workspace.LoadSolution(@&quot;HelloWorld.sln&quot;);
ISolution solution = workspace.CurrentSolution;</code></pre>

<p>host 환경에서 제공되는 workspace의 경우 host 가 알아서 특정 이벤트에 따라 update 시켜주지만, 위의 경우처럼 독립적으로 workspace를 만들었을 경우, 그 workspace는 본인이 직접 update 하지 않는한 스스로 외부 event에 따라 수정 되지 않습니다. </p>

<p>Solution도 마찬가지로, 직접 독립적으로 load 하실수 있습니다.</p>

<pre><code>ISolution solution = Solution.Load(@&quot;HelloWorld.sln&quot;);</code></pre>

<p>이 방법 말고도 원하신다면, emtpy solution으로부터 직접 원하시는 solution을 만드실 수도 있습니다.</p>

<pre><code>ProjectId pid1, pid2;
ISolution solution = 
    Solution.Create(SolutionId.CreateNewId(&quot;Solution&quot;))
    .AddCSharpProject(&quot;Project1.dll&quot;, &quot;Project1&quot;, out pid1)
    .AddDocument(pid1, &quot;A.cs&quot;, &quot;public class A { public int X; }&quot;)
    .AddCSharpProject(&quot;Project2.dll&quot;, &quot;Project2&quot;, out pid2)
    .AddDocument(pid2, &quot;B.cs&quot;, &quot;class B : A { public string Y; }&quot;)
    .AddProjectReference(pid2, pid1);</code></pre>

<h5><em>5.3 Working with a Solution</em></h5>

<p>solution은 어떤 한 순간의 host에 있는 모든 project와 document 정보의 snapshot입니다. </p>

<p>solution은 IProject들을 가지고 있는데 이 정보는 아래와 같은 방법들로 가져오실수 있습니다.</p>

<p>ProjectId를 아실때 이를 이용한 방법</p>

<pre><code>IProject project = solution.GetProject(projectId);</code></pre>

<p>project의 assembly 이름을 아실때 쓸수 있는 방법</p>

<pre><code>IProject project = solution.GetProjectByAssemblyName(&quot;Project1.dll&quot;);</code></pre>

<p>solution 안에 있는 모든 IProject들을 가져오는 방법</p>

<pre><code>foreach (IProject project in solution.Projects) { ... }</code></pre>

<p>이 각각의 project는 또 IDocument들을 가지고 있는데, 이 정보는 아래와 같은 방법으로 가져올수 있습니다.</p>

<p>DocumentId를 알때</p>

<pre><code>IDocument document = project.GetDocument(docId);</code></pre>

<p>project안에 있는 모든 IDocument들을 가져 올때</p>

<pre><code>foreach (IDocument document in project.Documents) { ... }</code></pre>

<p>이 document들을 통해 각 소스 파일을 여러 정보를 가져 올수 있습니다. 예를 들어 소스 코드는 아래와 같이 가져올수 있고</p>

<pre><code>IText text = document.GetText();</code></pre>

<p>소스 코드에 대응하는 syntax tree는 아래와 같이 가져올수 있고</p>

<pre><code>CommonSyntaxTree syntax = document.GetSyntaxTree();</code></pre>

<p>semantic model은 이렇게 가져 올수 있습니다.</p>

<pre><code>ISemanticModel semanticModel = document.GetSemanticModel();</code></pre>

<h5><em>5.4 Updating a Workspace</em></h5>

<p>만약 코드 분석후, 실제 host안의 소스 코드를 수정하려면, 결국 끝에는 workspace를 update해야 합니다. workspace를 update 하기 위해선 수정된 소스 코드를 가지고 있는 solution을 만들어야 합니다.</p>

<p>수정된 소스 코드를 가지고 있는 새 solution을 만드는 한가지 방법은 아래와 같습니다.</p>

<pre><code>IText newText = newRoot.GetFullTextAsIText();
ISolution newSolution = solution.UpdateDocument(docId, newText);</code></pre>

<p>일단 새 solution을 만들었으면, 이 solution을 이용하여 다른 분석을 또 할수도 있고, 아니면 이 새 solution에 들어 있는 change들을 workspace에 적용할수도 있습니다. 기존 solution을 이용해서 새 solution을 만드는게 자동으로 workspace를 수정하진 않습니다.</p>

<p>적용하길 원하시면 아래처럼 직접적으로 change들을 apply 하셔야 합니다.</p>

<pre><code>workspace.ApplyChanges(solution, newSolution);</code></pre>

<p>적용하실때 반드시, workspace 에서 얻은 원본 solution과 새 solution을 같이 제공해야 합니다.</p>

<h4>6 Working with Services</h4>

<p>지금까지 로즐린 compiler와 workspace API에 대해 알아보았습니다. 컴파일러와 워크스페이스 API 만 있으면 코드 분석과 코드 수정이 가능합니다. 하지만 많은 경우, 사람들은 VS extension을 만들거나, VS C#, VB IDE 기능들을 확장하길 원합니다. Service API는 이걸 쉽게 가능하게 해 줍니다. Service API를 통해 intellisense나 smart tag 혹은 error report를 확장하게 해 줍니다.</p>

<p>기타 layer와 다르게 Services APIs는 VS와 WPF editor에 의존적입니다.</p>

<h5><em>6.1 Key Concepts</em></h5>

<p>이번 섹션에서는 service API를 통해 refactoring, find all references, formatting, intellisense와 같은 서비스를 이용하고 확장하는 주요 콘셉에 대해 알아보겠습니다.</p>

<p><strong>6.1.1 Managed Extensibility Framework</strong></p>

<p>Service API는 VS extension을 이용하기 위해 MEF라고 알려진 <em>Managed Extensibility Framework </em>에 의존적입니다. MEF는 가볍고 확장 가능한 어플리케이션을 만들기 위한 라이브러리 입니다. application 개발자들은 MEF를 이용하여 아무런 configuration 없이 extensions들을 찾아내고 사용할수 있습니다. 또한 이를통해 extension 개발자들은 본인의 코드를 다른 코드에 의존성 없이 개발 할수 있으며, extension들이 application 안에서 다양한 형태로 재 사용 가능하게 해 줍니다. 또한 같은 extension이 다른 application에서도 재 사용 가능하도록 해 줍니다.</p>

<h5><em>6.2 Creating an IDE extension</em></h5>

<p>Services API는 Roslyn.Services.Editor namespace를 using 함으로써 사용하실수 있습니다.</p>

<pre><code>using Roslyn.Services.Editor;</code></pre>

<p>VS IDE 확장을 지원하기 위해서, Service API 는 여러 IDE 기능에 대응하는 확장 point를 제공합니다. completion list, code outliner, highlight references, syntax classifier 그리고 code refactoring 같은게 예가 되겠습니다. 이 각각의 기능들에 대해, 사용자들은 원하는 확장 기능에 대한 정보를 담은 Export attribute을 가진 MEF provider를 제공함으로써 IDE 기능을 확장 하실수 있습니다.</p>

<p>예로, 만약 본인만의 code refactoring을 만드시고 싶으시다면, <em>code issue</em> provider를 제공함으로써 자연스럽게 기존 IDE의 기능과 함께 자신이 만든 기능이 IDE의 error list, user suggestion에 나오도록 하실수 있습니다.</p>

<p>code issue provider의 MEF export를 예로 보면</p>

<pre><code>[ExportSyntaxNodeCodeIssueProvider(&quot;FirstQuickFixCS&quot;, 
		LanguageNames.CSharp, typeof(VariableDeclarationSyntax))]
class CodeIssueProvider : ICodeIssueProvider {}</code></pre>

<p>LanguageNames.CSharp를 통해 이 code issue provider가 C# 파일에서만 작용하게 할수 있고, VariableDeclarationSyntax를 통해 이 code issue provider가 VariableDeclarationSyntax node에서만 GetIssues가 불리게 할수 있습니다.</p>

<p>모든 code issue provider들은 아래와 같은 ICodeIssueProvider 인터페이스를 구현해야 하는데, 이 interface에는 노드, 토큰, 트리비아를 위한 각각의 GetIssues 함수가 있습니다. 위의 export에 사용된 attribute에 따라, Roslyn의 Code Issue Engine이 알아서 맞는 GetIssues를 불러주게 됩니다.이 경우는 ExportSyntaxNodeCodeIssueProvider가 사용됐으므로 노드를 위한 GetIssues가 불려지게 됩니다.</p>

<pre><code>public interface ICodeIssueProvider
{
	IEnumerable&lt;CodeIssue&gt; GetIssues(
               IDocument document, 
               CommonSyntaxNode node, 
 		     CancellationToken cancellationToken);
}</code></pre>

<p>원할 경우, CodeIssue는 issue를 고칠수 있는 code action을 제공할수 있는데, 밑의 ICodeAction을 구현 함으로써 code action을 만들수 있습니다.</p>

<pre><code>public interface ICodeAction
{
    string Description { get; }
    ImageSource Icon { get; }
    ICodeActionEdit GetEdit(CancellationToken cancellationToken);
}</code></pre>

<p>위의 여러 property를 구현 함으로써, code issue 툴팁, 에러 리스트, preview에 나오는 다양한 정보들을 구현 하실수 있습니다. Services API에 대한 보다 자세한 내용은 “How to Write a Quick Fix walkthrough” 와 Roslyn CTP에 들어 있는 샘플을 보시기 바랍니다.</p>

<h4>7 Summary</h4>

<p>프로젝트 로질린은 Compiler APIs, Scripting APIs, Workspace APIs, and Services APIs 와 같은 다양한 형태의 API들을 제공합니다. 이 API들을 통해 C#/VB 언어에 가장 근접한 형태로 소스에 대한 다양한 정보를 제공해 줍니다. compiler를 하나의 service (API) 형태로 제공함으로써, third party들이 코드에 관계된 툴과 application을 만드는데 장벽이 됐던 여러 요소들을 많이 낮춰 줍니다. 로즐린의 API를 통해 meta-programming, code 생성과 변환, C#과 VB의 interactive한 사용, C#과 VB를 다른 domain specific language에 임베드해서 사용등등 많은 innovation이 가능해질수 있는 첫문을 열었습니다.</p>

<p>&#160;</p>

<p>…………………………………</p>

<p>&#160;</p>

<p>위에까지가 번역입니다. 보시면 아시겠지만, 위의 내용은 정말 대략적입니다. 위의 내용중에 구체적인 내용을 아시고 싶으신걸 적으시면 제가 그 부분에 대한 더 자세한 blog를 올리도록 하겠습니다.</p>

<p>&#160;</p>

<p>수고</p>

<p>- 희제</p>]]></content:encoded>
    </item>
    <item>
      <title>Roslyn에서 Emit한 어셈블리 unload 하기</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/roslyn-emit-unload</link>
      <pubDate>Sun, 23 Oct 2011 18:15:45 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/23/roslyn-emit-unload/</guid>
      <description><![CDATA[이것도 어떤 분께서 하신 질문에 대한 좀 더 자세한 답변을 블로그로 올리는 것입니다. 앞에 블로그에도 썼듯이, 이 답변은 현재 Roslyn CTP에 공개된 내용만을 바탕으로...]]></description>
      <content:encoded><![CDATA[<p>이것도 어떤 분께서 하신 질문에 대한 좀 더 자세한 답변을 블로그로 올리는 것입니다. 앞에 블로그에도 썼듯이, 이 답변은 현재 Roslyn CTP에 공개된 내용만을 바탕으로 하는것이니 만큼 final version에서는 얼마든지 바뀔수 있습니다.</p>  <p>&#160;</p>  <p>일단 질문은 Roslyn으로 compile 에서 만들어진 어셈블리들은 일단 메모리에 load된 후에 unload가 가능한가 입니다. 일단 답변은 unload가 가능한것도 있고 불가능한것도 있다 입니다.</p>  <p>&#160;</p>  <p>일단 Roslyn에는 2가지 방식의 Emit이 있습니다. 첫번째는 <a href="https://research.microsoft.com/en-us/projects/cci/" target="_blank">CCI</a>를 이용한 Emit이고, 두번째는 <a href="https://msdn.microsoft.com/en-us/library/system.reflection.emit.aspx" target="_blank">Reflection</a>을 이용한 Emit입니다. 그중 CCI를 이용하는 Emit의 경우, 그 결과를 stream에 넣어줍니다. 이걸 메모리로 로드할 경우, 일반적인 어셈블리를 메모리로 로드한 경우와 같이, 후에 이 어셈블리만 unload 하실순 없습니다. 여태 해왔듯이 다른 app domain으로 로드 하신 후, 그 app domain을 내리셔야 합니다. Reflection을 이용하는 Emit의 경우, 자동으로 결과물을 메모리에 올리게 됩니다. 이때 가능하면 <a href="https://msdn.microsoft.com/en-us/library/dd554932.aspx" target="_blank">Collectible</a>한 다이나믹 어셈블리를 메모리에 올리게 됩니다. 콜렉터블한 어셈블리가 만들어졌는지 아닌지는, Emit이 돌려주는 ReflectionEmitResult.IsUncollectible로 확인하실수 있습니다. dynamic assembly와 collectible assembly에 대한 보다 자세한 내용은 <a href="https://msdn.microsoft.com/en-us/library/8ffc3x75.aspx" target="_blank">여기</a>에 나와 있습니다.</p>  <p>&#160;</p>  <p>간단하게 말해, Stream에 emit 하는 경우, 일반적인 다른 경우와 똑같고, 여태 해오시던데로 하시면 되고, Reflection을 통해 emit 할 경우, Roslyn에서 기존 .NET에서 제공하던 가장 밑단의 API 위에, 좀 더 사용하기 쉬운 Emit API를 올렸고, 가능한 한 후에 콜렉터블한 다이나믹 어셈블리를 emit 하지만, 만약 코드에서 콜렉터블이 블가능한 구문들을 사용했을 경우, 콜렉터블 하지 않은 어셈블리를 emit 한 후에, caller에게 그 사실을 알려준다는 것입니다. 어떤 경우 콜렉터블 하지 않는지는 위의 <a href="https://msdn.microsoft.com/en-us/library/dd554932.aspx" target="_blank">링크</a>에 설명 되어 있습니다.</p>  <p>&#160;</p>  <p>아무 신경 쓰고 싶지 않으시다면, 그냥 전처럼 다른 app domain에 올리신 후에, 맨 끝에 그 app domain 내리시면 됩니다. 어떤 분께서 언제 언로드가 제공될지 물으셨는데, 저도 앞으로 어찌될지는 모르겠습니다 <img style="style" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/52/56/metablogapi/0410.wlEmoticon-smile_39FA26E8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-52-56-metablogapi/0410.wlEmoticon_2D00_smile_5F00_39FA26E8.png" /></p>  <p>&#160;</p>  <p>참고로, 밑에는 Rolsyn.Compilers.CSharp.Compilation 에 있는 3개의 Emit 시그니쳐 입니다.</p>  <p>&#160;</p>  <p>public ReflectionEmitResult Emit(ModuleBuilder moduleBuilder, Roslyn.Compilers.IAssemblyLoader assemblyLoader = null, Func&lt;AssemblySymbol, AssemblyName&gt; assemblySymbolMapper = null, CancellationToken cancellationToken = null);</p>  <p>   <br />public EmitResult Emit(Stream executableStream, string pdbFileName = null, Stream pdbStream = null, Stream xmlDocStream = null, CancellationToken cancellationToken = null, Func&lt;string, Stream&gt; xmlNameResolver = null, Stream win32ResourcesInRESFormat = null, IEnumerable&lt;Roslyn.Compilers.ResourceDescription&gt; manifestResources = null);</p>  <p>   <br />public EmitResult EmitMetadataOnly(Stream metadataStream, Stream xmlDocStream = null, CancellationToken cancellationToken = null, Func&lt;string, Stream&gt; xmlNameResolver = null);</p>  <p>&#160;</p>  <p>감사합니다. 질문 주시면 또 블로그로 답변 드리도록 하겠습니다.</p>  <p>수고.</p>  <p>- 희제</p>]]></content:encoded>
    </item>
    <item>
      <title>Roslyn에서 ShadowCopy 서비스 사용하기</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/roslyn-shadowcopy</link>
      <pubDate>Sun, 23 Oct 2011 17:22:46 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/23/roslyn-shadowcopy/</guid>
      <description><![CDATA[어떤 분께서 질문하셔서, 간단하게 답변 드렸는데, 주말에 시간나서 블로그로 좀 더 자세히 답변 드릴려고 합니다.  * 여기서 한가지 짚고 넘어갈 것은 밑에 설명은 final 한...]]></description>
      <content:encoded><![CDATA[<p>어떤 분께서 질문하셔서, 간단하게 답변 드렸는데, 주말에 시간나서 블로그로 좀 더 자세히 답변 드릴려고 합니다.</p>  <p>* 여기서 한가지 짚고 넘어갈 것은 밑에 설명은 final 한 API에 대한 것이 아니기 때문에, 마지막 버젼에서는 아예 없어질수도 완전히 바뀔수도 있음을 알려드립니다.</p>  <p>&#160;</p>  <p>일단, <a href="https://msdn.microsoft.com/en-us/hh500769" target="_blank">여기</a>에 써 있는 여러 layer 중에 compiler layer (API) 만 쓰실 경우, reference들의 shadow copy를 원하시면 직접 구현 하셔야 합니다. 이 layer에선 AssemblyFileReference 를 만드실 경우 FileShare.Read 와 FileShare.Delete을 이용하여, 주신 파일 그 자체를 open 합니다. 따라서 당연히 수정 이런게 안되죠. 이게 중요한 이유는 만약 VS에서 컴파일 되는 live dlls 들을 reference 해서 뭔가 실시간 분석 프로그램을 만들 경우, 계속 그 reference들을 가지고 있는 한, VS에서 그 파일들을 다시 만들수가 없습니다. </p>  <p>&#160;</p>  <p>그럼 그럴경우 어떻게 해야 하느냐, 이럴때 필요한게 shadow copy인데, 이걸 뭐 직접 구현 하셔도 되고, 아니면 Workspace Layer(API)에서 제공하는 shadow copy service를 이용하셔도 됩니다.</p>  <p>&#160;</p>  <p>위에 글에 있다시피, 이번에 공개된 Roslyn은 여러 부분을 커버 하는데, 코드 자체에 대한 정보만을 제공하는 compiler API, 이것을 이용해서 scripting 환경을 구현하는 Script API, 코드에 대한 정보를 좀 더 high level (추상화된 상태)로 관리해 주는 Workspace API, 그리고 이런 정보들을 이용하여 좀 더 구체적인 service를 제공하는 Service API 이렇게 됩니다. 이 중에 Workspace 단에서 바로 이 shadow copy 를 맡게 됩니다.</p>  <p>&#160;</p>  <p>Workspace 단은 다시 Host 쪽에서 제공해야 기능들을 위한 API와 consumer 쪽에서 사용하는 API로 나뉩니다. 이 shadow copy는 바로 Workspace Host 단에서 제공해줘야 하는 서비스 입니다. 그 서비스는 Rolsyn.Services.Host.IWorkspaceService 라는 메카니즘을 이용하고, shadow copy를 제공하는 서비스는 Roslyn.Services.Host.IMetadataFileProviderService 라는 interface를 implement 하게 됩니다.</p>  <p>&#160;</p>  <p>로즐린에 들어있는 가장 basic implementation은 shadow copy를 제공하지 않는것입니다. VS에 들어있는 방법은 아주 간단한 shadow copy 기능을 제공하는것이죠. 현재로 이미 Host에서 제공된 이 서비스를 override 하실 방법은 없습니다. 하지만, 본인이 직접 Roslyn.Services.Host.HostWorkspace 를 상속해서 자신만의 Workspace를 만들 경우, 자신이 원하는 IMetadataFileProviderService를 제공함으로써 Shadow Copy 를 이용하실순 있습니다.</p>  <p>&#160;</p>  <p>자신만의 Workspace + Shadow Copy 를 원할 경우, 정말 간단하게 shadow 카피만을 원하신다면, Roslyn.Scripting.AssemblyShadowCopyProvider 라는 Scripting 쪽에서 기본으로 제공하는 서비스를 그냥 재사용하셔도 무방할것으로 보입니다.</p>  <p>&#160;</p>  <p>여기 내용 중 더 자세하게 알고 싶으신 부분을 말씀해 주시면 또 블로그 포스팅 올리도록 하겠습니다.</p>  <p>&#160;</p>  <p>수고.</p>  <p>- 희제</p>]]></content:encoded>
    </item>
    <item>
      <title>Roslyn CTP 가 오늘 공식적으로 발표 되었습니다.</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/roslyn-ctp</link>
      <pubDate>Wed, 19 Oct 2011 13:15:00 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/19/roslyn-ctp/</guid>
      <description><![CDATA[안녕하세요. 오늘 VSIP Summit 에서 로즐린 CTP가 공개되었습니다.
오늘은 간단하게 재미있는 링크만 몇개 쭉 달아 놓겠습니다. 주말쯤 블로그 쓰는 시간에 저도 몇개...]]></description>
      <content:encoded><![CDATA[<p>안녕하세요. 오늘 VSIP Summit 에서 로즐린 CTP가 공개되었습니다.</p>
<p>오늘은 간단하게 재미있는 링크만 몇개 쭉 달아 놓겠습니다. 주말쯤 블로그 쓰는 시간에 저도 몇개 올리도록 하겠습니다.</p>
<p>그럼 수고!</p>
<p>- 희제.</p>
<p>&nbsp;</p>
<p>메인 사이트 - <a href="https://msdn.com/roslyn">https://msdn.com/roslyn</a></p>
<p>&nbsp;</p>
<p>블로그들</p>
<p><a title="https://blogs.msdn.com/b/somasegar/archive/2011/10/19/roslyn-ctp-available-now.aspx" href="https://blogs.msdn.com/b/somasegar/archive/2011/10/19/roslyn-ctp-available-now.aspx">https://blogs.msdn.com/b/somasegar/archive/2011/10/19/roslyn-ctp-available-now.aspx</a></p>
<p><a title="https://blogs.msdn.com/b/ericlippert/archive/2011/10/19/the-roslyn-preview-is-now-available.aspx" href="https://blogs.msdn.com/b/ericlippert/archive/2011/10/19/the-roslyn-preview-is-now-available.aspx">https://blogs.msdn.com/b/ericlippert/archive/2011/10/19/the-roslyn-preview-is-now-available.aspx</a></p>
<p><a title="https://blogs.msdn.com/b/visualstudio/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx" href="https://blogs.msdn.com/b/visualstudio/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx">https://blogs.msdn.com/b/visualstudio/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx</a></p>
<p><a title="https://blogs.msdn.com/b/vbteam/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx" href="https://blogs.msdn.com/b/vbteam/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx">https://blogs.msdn.com/b/vbteam/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx</a></p>
<p><a title="https://blogs.msdn.com/b/csharpfaq/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx" href="https://blogs.msdn.com/b/csharpfaq/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx">https://blogs.msdn.com/b/csharpfaq/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx</a></p>
<p>&nbsp;</p>
<p>아직 들어가지 못한 기능들</p>
<p><a title="https://social.msdn.microsoft.com/Forums/en-US/roslyn/thread/f5adeaf0-49d0-42dc-861b-0f6ffd731825" href="https://social.msdn.microsoft.com/Forums/en-US/roslyn/thread/f5adeaf0-49d0-42dc-861b-0f6ffd731825">https://social.msdn.microsoft.com/Forums/en-US/roslyn/thread/f5adeaf0-49d0-42dc-861b-0f6ffd731825</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Await으로 Pause and Play 하기</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/await-pause-and-play</link>
      <pubDate>Sun, 16 Oct 2011 19:54:11 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/16/await-pause-and-play/</guid>
      <description><![CDATA[- 이번 글은 Mads Torgersen의 MSDN 글을 번역/요약한 것입니다.  매드가 쓴 글은 await의 의미가 무엇이며 내부적으로 어떻게 작동하는지, 개념적 부분 부터 실제...]]></description>
      <content:encoded><![CDATA[<p align="right">- 이번 글은 Mads Torgersen의 <a href="https://msdn.microsoft.com/en-us/magazine/hh456403.aspx" target="_blank">MSDN 글을</a> 번역/요약한 것입니다.</p>  <p align="left">매드가 쓴 글은 await의 의미가 무엇이며 내부적으로 어떻게 작동하는지, 개념적 부분 부터 실제 작동 원리까지 설명한 글입니다. 일단 글은 VB나 C# 처럼 imperative programming language의 특징을 설명하는것으로 부터 시작합니다. </p>  <p align="left">&#160;</p>  <p align="left"><font color="#4f81bd"><strong>Sequential Composition</strong></font></p>  <p align="left">Imperative programming language는 일단 프로그램을 순차적인 스텝의 나열로 짜는 것을 그 근본 방법으로 사용합니다 . 그렇기 때문에 코드의 대부분은 control을 조절하는데 쓰이게 되죠. 이럴테면, if, switch, loop, continue, break, goto 등등의 구문들로 어떤 부분의 코드가 실행 될지 하나씩 지정하게 되는거죠. 따라서 보통 이쪽 언어를 사용하는 개발자들은 흐름이 명확하고 간결하게 되어 있기를 원하고 그렇기 때문에 이런 종류의 control structure를 많이 지원하게 됩니다.</p>  <p align="left">&#160;</p>  <p align="left"><font color="#4f81bd"><strong>Continuous Execution</strong></font></p>  <p align="left">imperative programming 언어의 또하나의 특징은 연속성입니다. 일단 어떤 한 method가 thread에서 실행이 시작되면 그 method가 끝나기 전까진 control이 시스템으로 돌아가지 않습니다. 따라서 만약 코드 중에 blocking call이 들어 있다면 그 thread는 unblock 될때 까지 아무것도 안하고 가만히 있게 되는것이죠. blocking API의 예는 많습니다. 이를테면 인터넷에서 파일을 다운로드 받는걸수도 있고, 하드에서 파일을 읽어들이는걸수도 있고요. 특히 UI Thread의 경우는 이 연속성이 UI의 반응에 문제가 되는 경우가 많이 있습니다. block된 백그라운드 thread 역시 UI thread 만큼 문제입니다. 잘못하면 엄청난 수의 백그라운드 Thread들 때문에 되려 성능저하가 일어날수도 있으니까요.</p>  <table border="1" cellspacing="0" cellpadding="2" width="400"><tbody>     <tr>       <td valign="top" width="400">         <p>static byte[] TryFetch(string url)           <br />{            <br />&#160; var client = new WebClient();            <br />&#160; try            <br />&#160; {            <br />&#160;&#160;&#160; return client.DownloadData(url);            <br />&#160; }            <br />&#160; catch (WebException) { }            <br />&#160; return null;            <br />}</p>       </td>     </tr>   </tbody></table>  <p align="left"><font color="#4f81bd"><strong></strong></font></p>  <p align="left"><font color="#4f81bd"><strong>Asynchronous Programming</strong></font></p>  <p align="left">이런 문제의 해결 방법으로 나타난게 비동기식 프로그램밍입니다. 기존 언어가 가지고 있는 연속성의 특성을 부수는 것이죠. 이 연속성을 부수는 방법은 혹은 부수는것과 같은 효과를 얻을수 있는 방법은 여러가지가 있을수 있습니다. 기존의 event 바탕의 비동기식을 사용할수도 있고, TPL을 이용해서 프로그램머 스스로 callback으로 코드 흐름을 control 할수도 있고, 그 외 여러가지 방법들 (<a title="https://en.wikipedia.org/wiki/Coroutine" href="https://en.wikipedia.org/wiki/Coroutine">https://en.wikipedia.org/wiki/Coroutine</a>, <a title="https://en.wikipedia.org/wiki/Fiber_(computer_science)" href="https://en.wikipedia.org/wiki/Fiber_(computer_science">https://en.wikipedia.org/wiki/Fiber_(computer_science)</a>) 이 있을수 있습니다. 그러나 기존 방법들은 밑에 코드 예에서 보시듯, imperative programming 언어가 가지고 있는 순차적 스텝의 나열이라는 특징을 잃게 됩니다. 더 이상 콘트롤 구문들을 가지고 코드의 흐름을 간결하게 표현할수 없게 되는것이죠.</p>  <table border="1" cellspacing="0" cellpadding="2"><tbody>     <tr>       <td valign="top">         <p>static void TryFetchAsync(string url, Action&lt;byte[], Exception&gt; callback)            <br />{             <br />&#160;&#160;&#160; var client = new WebClient();             <br />&#160;&#160;&#160; client.DownloadDataCompleted += (_, args) =&gt;             <br />&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (args.Error == null) callback(args.Result, null);             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; else if (args.Error is WebException) callback(null, null);             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; else callback(null, args.Error);             <br />&#160;&#160;&#160; };             <br />&#160;&#160;&#160; client.DownloadDataAsync(new Uri(url));             <br />}</p>       </td>     </tr>   </tbody></table>  <p align="left">C# 5.0 Async는 이런 여러가지 비동기식 프로그램밍 방식 중 언어 자체가 가지고 있는 특징을 유지하면서 비동기식 프로그램밍을 지원하기 위해 C#/VB 언어 자체에 들어간 새로운 기능입니다.</p>  <p align="left">&#160;</p>  <p align="left"><font color="#4f81bd"><strong> Asynchronous Methods</strong></font></p>  <p align="left">다음 코드 예를 보시면 C# 5.0의 async가 어떻게 기존 언어의 특징을 그대로 유지하면서 비동기식 프로그램밍을 지원하는지 직관적으로 아실수 있으실 겁니다.</p>  <table border="1" cellspacing="0" cellpadding="2"><tbody>     <tr>       <td valign="top">         <p>static <em><strong>async</strong></em> Task&lt;byte[]&gt; TryFetchAsync(string url)             <br />{             <br />&#160; var client = new WebClient();             <br />&#160; try             <br />&#160; {             <br />&#160;&#160;&#160; return <em><strong>await</strong></em> client.DownloadDataTaskAsync(url);             <br />&#160; }             <br />&#160; catch (WebException) { }             <br />&#160; return null;             <br />}</p>       </td>     </tr>   </tbody></table>  <p>위에서 보듯이 기존 코드와 동일한 방식으로 코드의 흐름, exception 처리등을 비동기식으로 할 수 있게 해 주는것이죠.</p>  <p>&#160;</p>  <p align="left"><font color="#4f81bd"><strong>Compiler Rewriting</strong></font></p>  <p align="left">그럼 어떻게 이것을 가능하게 하느냐. 기존의 yield 처럼, compiler가 코드를 분석해서 code를 rewrite 함으로써 가능하게 되는것입니다. 다시 말해 async/await이 사용된 method를 compiler가 일종의 state machine으로 만들어서, await 부분을 만날때 마다, 현재 thread의 context를 다 저장했다가 await 했던게 리턴하면 다시 context 를 restore 시키고, await 다음 부터 다시 실행 시키는것이죠. 그래서 제목이 pause and play 입니다. 그럼 좀 더 자세히 이 pause and play가 어떻게 구현 됐는지 알아보겠습니다.</p>  <p align="left">&#160;</p>  <p align="left"><font color="#4f81bd"><strong>Task Builders</strong></font></p>  <p align="left">C#의 async를 사용한 비동기식 method는 .NET 4.0 에 들어간 Task/Task&lt;T&gt;를 리턴하게 됩니다. 이 리턴되는 Task는 이번에 새로 들어간 Task Builder라는 타입을 이용하여 compiler가 만들게 됩니다. 간단히 가상으로 컴파일러가 위의 예제를 TaskBuilder를 사용하도록 바꾼 코드를 보도록 하겠습니다.</p>  <table border="1" cellspacing="0" cellpadding="2"><tbody>     <tr>       <td valign="top">         <p>static Task&lt;byte[]&gt; TryFetchAsync(string url)            <br />{             <br />&#160; var __builder = new AsyncTaskMethodBuilder&lt;byte[]&gt;();             <br />&#160; ...             <br />&#160; Action __moveNext = delegate             <br />&#160; {             <br />&#160;&#160;&#160; try             <br />&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160; ...             <br />&#160;&#160;&#160;&#160;&#160; return;             <br />&#160;&#160;&#160;&#160;&#160; ...             <br />&#160;&#160;&#160;&#160;&#160; __builder.SetResult(…);             <br />&#160;&#160;&#160;&#160;&#160; ...             <br />&#160;&#160;&#160; }             <br />&#160;&#160;&#160; catch (Exception exception)             <br />&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160; __builder.SetException(exception);             <br />&#160;&#160;&#160; }             <br />&#160; };             <br />&#160; __moveNext();             <br />&#160; return __builder.Task;             <br />}</p>       </td>     </tr>   </tbody></table>  <p>위의 코드는 위의 예제 코드가 대충 어떤 모습으로 바뀌는지만 있을 뿐 return 되고 나서 어떻게 resume 될지, 이 return 한 task가 어떤 식으로 await 되는지는 나와 있지 않습니다. 그 부분은 다음 섹션에서 다룰것입니다.</p>  <p>&#160;</p>  <p align="left"><font color="#4f81bd"><strong>Awaitables and Awaiters</strong></font></p>  <p align="left">위에서 C#의 async를 이용한 비동기식&#160; method는 task를 이용한다고 했습니다. 하지만 이말이 await이 task만을 기다릴수 있다는 말은 아닙니다. C#의 await은 awaitable 한 타입은 모두 await 할수 있습니다. 이게 winRT에 Task 타입이 없음에도 C# await으로 winRT의 IAsyncOperation을 사용할수 있거나 F# 처럼 다른 방식의 비동기를 쓰는 언어와 함께 사용할수 있는 이유입니다. 물론 다른 비동기식 방법을 Task로 감싸는 방법도 있습니다. 정리하면, aysnc method로 부터&#160; awaitable 할수 있는 형태의 awaiter를 task로 부터 가져와서, 그 awaiter가 이미 실행이 종료 됐나 체크 하고 만약 되지 않았으면, context와 함께 save하고 control을 시스템으로 리턴, 기다렸던 method의 실행이 끝나면 다시 원 위치에 다 restore하고 resume 하게 되는겁니다.</p>  <table border="1" cellspacing="0" cellpadding="2"><tbody>     <tr>       <td valign="top">         <p>__awaiter1 = client.DownloadDataTaskAsync(url).GetAwaiter();            <br />&#160; if (!__awaiter1.IsCompleted) {             <br />&#160;&#160;&#160; ... // Prepare for resumption at Resume1             <br />&#160;&#160;&#160; __awaiter1.OnCompleted(__moveNext);             <br />&#160;&#160;&#160; return; // Hit the &quot;pause&quot; button             <br />&#160; }             <br />Resume1:             <br />&#160; ... __awaiter1.GetResult()) ...</p>       </td>     </tr>   </tbody></table>  <p align="left">대충 이런 종류의 코드가 generate 되는것이죠.</p>  <p align="left">&#160;</p>  <p align="left"><font color="#4f81bd"><strong>The State machine</strong></font></p>  <p align="left">위의 여러가지 단계들을 다 거치게 되면, 대략 이런 형태의 코드가 마지막에 생성되게 됩니다.</p>  <table border="1" cellspacing="0" cellpadding="2" width="400"><tbody>     <tr>       <td valign="top" width="400">         <p>static Task&lt;byte[]&gt; TryFetchAsync(string url)            <br />{             <br />&#160; var __builder = new AsyncTaskMethodBuilder&lt;byte[]&gt;();             <br />&#160; int __state = 0;             <br />&#160; Action __moveNext = null;             <br />&#160; TaskAwaiter&lt;byte[]&gt; __awaiter1;             <br />&#160; <br />&#160; WebClient client = null;             <br />&#160; <br />&#160; __moveNext = delegate             <br />&#160; {             <br />&#160;&#160;&#160; try             <br />&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160; if (__state == 1) goto Resume1;             <br />&#160;&#160;&#160;&#160;&#160; client = new WebClient();             <br />&#160;&#160;&#160;&#160;&#160; try             <br />&#160;&#160;&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; __awaiter1 = client.DownloadDataTaskAsync(url).GetAwaiter();             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!__awaiter1.IsCompleted) {             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; __state = 1;             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; __awaiter1.OnCompleted(__moveNext);             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return;             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Resume1:             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; __builder.SetResult(__awaiter1.GetResult());             <br />&#160;&#160;&#160;&#160;&#160; }             <br />&#160;&#160;&#160;&#160;&#160; catch (WebException) { }             <br />&#160;&#160;&#160;&#160;&#160; __builder.SetResult(null);             <br />&#160;&#160;&#160; }             <br />&#160;&#160;&#160; catch (Exception exception)             <br />&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160; __builder.SetException(exception);             <br />&#160;&#160;&#160; }             <br />&#160; };             <br />&#160; <br />&#160; __moveNext();             <br />&#160; return __builder.Task;             <br />}</p>       </td>     </tr>   </tbody></table>  <p>이런 난해한 코드가 생성되는 이유는 성능 때문입니다. 이를테면 await이&#160; 아주 간단한 method를 기다릴 경우, 비동기식 없이 바로 실행 할수 있게 하거나, allocation을 한번만 하고 재 사용한다거나 등등의 이유죠. 하지만, 보시면 알겠지만, 생각보다 원래 코드를 완전 뒤죽 박죽 만들진 않습니다. 대충 보면 아 이런식으로 하는구나 알수 있죠. 물론 그렇다고 보이는것 만큼 간단하지도 않습니다. </p>  <p>&#160;</p>  <p align="left"><font color="#4f81bd"><strong>The Fine Print</strong></font></p>  <p>위의 최종적으로 생성된 코드 예제는 사실 약간 심플화된 코드 입니다. 사실 저대로는 작동하지 않죠. 위에서 처럼 goto statement을 사용할수는 없습니다. 또 finally block 처럼 method에서 exit 할때 자동으로 실행 되는 코드는 약간의 특별한 관리를 요구하죠. 또 코드의 execution order도 resume 할때 유지 시켜 주어야 합니다. 예를 들어, method 중간에 await 때문에 현 method를 exit 한다면, 현 위치에 있는 finally block을 실행 해서는 안되고, 실행 해야 한다는 정보만 유지했다 나중에 resume 한 후에 실행 시켜야 한다던지, 다른 method 콜을 위해 method argument를 stack에 올리고 있었다면 그 정보도 save했다 restore 해줘야 하고, local들중 필요한건 lift해야 하고, 등등. 나중에 resume 될때 thread context도 유지 시켜줘야 하는 문제도 있죠. 이렇게 단순하지 않기 때문에 특정 부분 예를 들어 catch나 finally 구문 안에서는 await를 사용 할수 없습니다.</p>  <p>&#160;</p>  <p align="left"><font color="#4f81bd"><strong>The Task Awaiter</strong></font></p>  <p align="left">.NET에서 기본으로 제공하는 Task Awaiter를 본인이 직접 만든걸 쓰면, 위에 쓴것 보다 많은 자유도를 가질수 있게 됩니다만, Awaiter를 제대로 만드는것은 쉽지 않기 때문에, 추천하지 않습니다. 일반적으로 Task Awaiter를 직접 만들려는 이유는 thread context 때문인데, 이 경우, TPL에서 제공하는 Task Scheduling Context를 이용하는 방법이 훨씬 간단합니다. 보다 자세한 내용은 TPL을 참고하는게 좋을꺼 같습니다. 간단히 말해, 어떤 thread에서 pause되고 어떤 thread에서 resume 될것인가? 둘 사이에 marshaling은 어찌 할 것인가. await이 작동하는 thread들은 어떤식으로 관리될것인가 이 모두를 TPL의 task scheduler나 .NET의 synchronization context를 이용하여 조절할수 있습니다.</p>  <p align="left">&#160;</p>  <p align="left"><font color="#4f81bd"><strong>Go Forth and Async’ify</strong></font></p>  <p>자 이제 마지막, 이제까지 C#의 async가 어찌 작동하는지 알아보았답니다. 가장 중요한 포인트 3가지는</p>  <ol>   <li>컴파일러가 유저 코드의 흐름을 비동기 임에도 불구하고 그대로 유지해 준다</li>    <li>비동기는 멀티 쓰레드가 아니다. 하나의 쓰레드를 여러개로 쪼개쓸수 있도록 해준다.</li>    <li>await 됐던 코드가 다시 실행 될때, 이 코드는 await 되기 이전의 상태와 거의 동일한 context에서 다시 실행된다.</li> </ol>  <p>이상입니다.!</p>  <p>&#160;</p>  <p>….</p>  <p>다 좋은데, 이 글은 C# async가 내부적으로 어찌 돌아가는지는 나와 있는데, 일반적으로 어떻게 사용하는지에 대한 내용은 없네요. 그런 내용은 이미 오래전부터 많이 다뤄져서 없나 봅니다. 그런 글 찾으면 나중에 또 올리도록 하겠습니다.</p>  <p>그럼 수고!</p>  <p>- 희제</p>]]></content:encoded>
    </item>
    <item>
      <title>Async CTP와 함께하는 좀 더 쉬운 비동기 프로그램밍</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/async-ctp</link>
      <pubDate>Sun, 09 Oct 2011 21:56:29 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/09/async-ctp/</guid>
      <description><![CDATA[- 이글은 Eric Lippert가 쓴 “Easier Asynchronous Programming with the New Visual Studio Async CTP” 글의...]]></description>
      <content:encoded><![CDATA[<p align="right">- 이글은 Eric Lippert가 쓴 “<a href="https://msdn.microsoft.com/en-us/magazine/hh456401.aspx" target="_blank">Easier Asynchronous Programming with the New Visual Studio Async CTP</a>” 글의 번역/요약본 입니다.</p>  <p align="left">&#160;</p>  <p align="left">에릭이 쓴 글은 Async 자체에 대한 기술적인 글이라기 보단 왜 Async가 다른 패턴보다 나은가에 대한 글입니다. 아주 간단한 코드 snippet로 부터 글을 시작합니다.</p>  <table border="1" cellspacing="0" cellpadding="2"><tbody>     <tr>       <td valign="top">         <p>void ServeBreakfast(Customer diner)            <br />{             <br />&#160;&#160;&#160; var order = ObtainOrder(diner);             <br />&#160;&#160;&#160; var ingredients = ObtainIngredients(order);             <br />&#160;&#160;&#160; var recipe = ObtainRecipe(order);             <br />&#160;&#160;&#160; var meal = recipe.Prepare(ingredients);             <br />&#160;&#160;&#160; diner.Give(meal);             <br />}</p>       </td>     </tr>   </tbody></table>  <p>위의 코드는 정말 어디서나 볼수 있는 단순한, 정말 아무런 문제가 없어 보이는 코드죠. 하지만 만약 위의 코드중 하나의 method call (ex – ObtainIngredients)가 오래 걸린다면, 그리고 ServeBreakfast가 UI Thread에서 불린다면? 붐. 모두가 windows app에서 싫어 하는 그 하얀 바탕 화면의 unresponsive app이 되는거죠. 물론 여기에 다른 이슈들을 가미 시킨다면 위의 정말 단순해 보이는 코드가 또 다른 여러가지 문제들을 야기 시킬수 있겠죠. 이를테면, 한번에 하나만의 ServeBreakfast 코드가 UI에서 작동할수 있다던지, 동시에 진행할수 있는 일을 순차적으로만 실행한다던지 말이죠. 정말 단순해 보이는 코드도 UI라는 특수한 상황에 비추어 생각을 해 보면 간단하지가 않게되는거죠.</p>  <p>&#160;</p>  <p>위의 문제 제기후, Eric은 현재까지 존재 하는 여러 방식의 해결책을 제시하면서 각 해결책이 가지고 있는 문제점을 지적합니다.</p>  <p>첫번째 해결 방식은 multi-thread를 사용하는 방식입니다. </p>  <p>이 해결책은 동시에 여러 ServeBreakfast 코드를 실행 할수 있는 방법을 제공하지만, 그와 함께 정말 많은 문제 역시 가지고 있습니다. 이를테면, 각각의 thread가 너무 무겁다는 거죠. default로 각 스레드마다 1메가의 stack 메모리를 가지고 가니까요, thread를 사용한다고 할지라도 UI Thread처럼 thread affinity가 있는 경우는 특정 call을 위해 context를 save 시켜야 하는 일도 프로그램머가 알아서 해 줘야 하고요 (SynchronizationContext, Control.Invoke, Dispatcher), 이걸로 인해 race condition이나 dead lock 문제가 생길수 있고요. 특히 여러개의 ServeBreakfast를 동시에 실행할수 있게만 해 줄 뿐이지, 그 안에서 blocking call이 있을 경우 Thread가 waste 되는건 여전히 막을수 없죠. 이런 여러가지 문제를 복합적으로 볼때 multi-thread로 UI Thread의 반응을 좋게 하려 할 경우, 코드가 완전 복잡해 질뿐 아니라, 실제 얼마나 더 반응성이 좋아지는지는 in practice에선 확실치 않죠.</p>  <p>&#160;</p>  <p>두번째 해결 방식은 Application.DoEvents를 사용하는 방식입니다.</p>  <p>이 방식은 뭐 이런식으로 사용하는건데요. 일단 봐도 코드가 좀 드러워 보입니다. 땜빵 같아 보이죠.</p>  <table border="1" cellspacing="0" cellpadding="2"><tbody>     <tr>       <td valign="top">         <p>void ServeBreakfast(Customer diner)            <br />{             <br />&#160;&#160;&#160; var order = ObtainOrder(diner);             <br />&#160;&#160;&#160; Application.DoEvents();             <br />&#160;&#160;&#160; var ingredients = ObtainIngredients(order);             <br />&#160;&#160;&#160; Application.DoEvents();             <br />&#160;&#160;&#160; var recipe = ObtainRecipe(order);             <br />&#160;&#160;&#160; Application.DoEvents();             <br />&#160;&#160;&#160; var meal = recipe.Prepare(ingredients);             <br />&#160;&#160;&#160; Application.DoEvents();             <br />&#160;&#160;&#160; diner.Give(meal);             <br />}</p>       </td>     </tr>   </tbody></table>  <p>간단한 경우, 특히나 loop인 경우, DoEvent가 잘 작동하기도 합니다만, 조금만 복잡한 경우, DoEvents는 재앙의 시작이죠. 일단 맨 처음 예처럼 ObtainIngredients가 오래걸린다 해도 이 DoEvents는 아무런 도움이 되지 못하죠. ServeBreakfast 중간 중간에 또 다른 ServeBreakfast가 실행 될수 있음은 당연하고요 (reentrancy). context를 explicit하게 save하지 않고, 어쩌다 stack에 있으니까 save 되긴 합니다만, 하여간 그 문제가 있구요. DoEvents가 message queue가 빌때까지 pumping 하니까, 나중에 run한 ServeBreakfast가 항상 오리지날 보다 먼저 끝나게 되는 문제들이 발생할수 있구요. 결론은 첫번째 방식 만만찮게 이것도 UI 반응성 하나를 높이기 위해 프로그램이 불안정해지거나 혹은 많은 양의 defensive 코드를 써서 reentrancy의 불안정 문제를 없앤다 해도, 로직을 완전 복잡하게 만들어 버리는 사태가 발생할수 있죠.</p>  <p>&#160;</p>  <p>세번째 해결 방식은 lambda를 이용한 callback 방식입니다.</p>  <p>이건 가장 최근에 트랜드 탄 방식인데요. 특히 람다 (C# 3.0) 와 함께 Task가 소개된 .NET 4 부터 유행한 방식이죠. 일단 단순한 작업을 할때 정말 쉽게 적은 양의 코드로 문제를 해결할수 있고, Task가 context 및 dependency 그리고 synchonization을 간편하게 관리할수 있게 해 줌으로써 패러럴 프로그램밍과 비동기 프로그램밍의 대중화에 한발 다가 설수 있게 해줬죠. 하지만 이것 역시 그 나름의 문제가 있습니다. 간단할땐 모르지만, 일단 로직이 복잡해지면, 코드를 이런식으로 짜야 하는 문제가 생기고. 또 exception이나 기타 loop, try/catch 등과 같은 language construct들과 어떤 식으로 믹스 해야할지가 분명하지 않습니다.</p>  <table border="1" cellspacing="0" cellpadding="2" width="400"><tbody>     <tr>       <td valign="top" width="400">         <p>void ServeBreakfast(Diner diner)            <br />{             <br />&#160;&#160;&#160; ObtainOrderAsync(diner, order =&gt;             <br />&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; ObtainIngredientsAsync(order, ingredients =&gt;             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ObtainRecipeAsync(order, recipe =&gt;             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; recipe.PrepareAsync(ingredients, meal =&gt;             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; diner.Give(meal);             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; });             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; });             <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; });             <br />&#160;&#160;&#160; });             <br />}</p>       </td>     </tr>   </tbody></table>  <p>위의 코드를 간단하게 설명하자면, diner로 부터 order를 가져오는 함수를 실행 시킨 후, 그 함수가 끝난후 실행해야 할 코드를 lambda로 준 후, control를 OS로 넘겨줍니다. (message pumping을 하게 해준다는 말입니다.) 이건 두번째 방법의 재진입과는 조금 다릅니다. 현 callstack안에서 또 다른 message dispatching을 하는게 아니라, 현 context를 다 save 한 후에, call을 exit 해서 app의 메인 message pumping으로 control를 돌려주는겁니다. 기존 app 의 structure와 control flow가 같기 때문에 재진입을 특별히 신경쓰지 않은 기존의 코드들과 같이 사용해도 문제가 될 확율이 훨씬 적습니다. order를 가져오는 함수의 실행이 끝나면, 알아서 lambda가 실행되고, 이번엔 order로 부터 ingredients가 가져오는 함수가 끝난후 실행 되어야 할 lambda를 준 후 다시 os로 control를 돌려주는것이죠. 본문에는 말하지 않았지만, 이것 역시 UI context 문제는 그대로 있습니다. 물론 TPL이 그걸 좀 더 쉽게 관리해 주는 방법을 제공하고, 각 Task가의 dependency 관리를 제공해 주니까 개발자가 일일이 명시적으로 코딩하는것 보단 훨씬 수월하죠. 정말 원하면 Task를 이용해서 좀 flow logic을 알아보기 쉽게 코드 할수도 있지만, 여전히 위에 써 놓은 문제들과, 순차적 프로그램밍에 익숙한 대부분의 개발자에게 이질적으로 보이는 문제를 해결할수는 없습니다.</p>  <p>&#160;</p>  <p>마지막 해결 방식이 이번에 새로 들어간 async/await 해결 방식입니다.</p>  <p>이 방식은 위의 3번째 해결 방식을 언어 자체에 집어 넣어 준 것이죠. 보다 자세한 기술 적인 사항은 다음 포스트에 쓰기로 하겠습니다. 여기선 간단히 요약해서 쓰면, 밑에 처럼 일반적인 순차적 프로그램밍 방식으로 코딩을 await과 같이 하면, compiler가 알아서 밑에 코드를 3번 해결책 처럼 rewrite해 주는 겁니다. 물론 동시에 현 시점의 stack과 여러 context를 같이 save 해 줘서, 다음 코드가 실행 될때, 전과 동일한 상태로 만들어 주는것이죠. 물론 여기도 문제는 있습니다. global state이 await을 사이로 바뀔수 있다는 점인데. 이건 기존의 다른 어떤 방식도 다 가지고 있는 문제이니 딱히 이 해결책 만의 문제라고 볼쑨 없죠.</p>  <table border="1" cellspacing="0" cellpadding="2" width="400"><tbody>     <tr>       <td valign="top" width="400">         <p>async void ServeBreakfast(Diner diner)            <br />{             <br />&#160;&#160;&#160; var order = await ObtainOrderAsync(diner);             <br />&#160;&#160;&#160; var ingredients = await ObtainIngredientsAsync(order);             <br />&#160;&#160;&#160; var recipe = await ObtainRecipeAsync(order);             <br />&#160;&#160;&#160; var meal = await recipe.PrepareAsync(ingredients);             <br />&#160;&#160;&#160; diner.Give(meal);             <br />}</p>       </td>     </tr>   </tbody></table>  <p>하여간, 위에 코드를 보시면 알겠지만, 기존에 나와 있던 어떤 방식보다 포커스를 코드 로직 자체에 그대로 둘수 있고, control flow가 가장 명확하면서 여타 잡달스러운 문제점으로 부터 벗어날수 있는 방식입니다. 재진입의 문제도 없고, multi-thread를 사용하지 않아도 되고 (원하면 사용할수도 있고), 사용자가 직접 context와 각 코드간의 의존성을 신경쓰지 않아도 되고, 동기화도 알아서 제공해주는, 거기다 콘트롤 순서도 직관적인 제 개인적인 의견으로 가장 이상적인 비동기 프로그램밍 방식입니다.</p>  <p>&#160;</p>  <p>Async/await의 실제 사용 방법의 기술적인 내용은 다음 포스트에서 올리도록 하겠습니다.</p>  <p>&#160;</p>  <p>수고.</p>]]></content:encoded>
    </item>
    <item>
      <title>C# Async 에 관련된 글들.</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/c-async</link>
      <pubDate>Sun, 09 Oct 2011 17:50:39 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2011/10/09/c-async/</guid>
      <description><![CDATA[예전부터 C# Async에 대한 글들을 올리고 싶었는데, 게을러서 못하다가 이번에 맘 먹고 올려야지 하는 찰라, 우연히 이번 달 MSDN Magazine을 봤는데, 이번 달 주제가...]]></description>
      <content:encoded><![CDATA[<p>예전부터 C# Async에 대한 글들을 올리고 싶었는데, 게을러서 못하다가 이번에 맘 먹고 올려야지 하는 찰라, 우연히 이번 달 MSDN Magazine을 봤는데, 이번 달 주제가 Async 프로그램밍이네요. <img class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/52/56/metablogapi/4572.wlEmoticon-smile_58FC9E5D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-52-56-metablogapi/4572.wlEmoticon_2D00_smile_5F00_58FC9E5D.png" /></p>  <p>&#160;</p>  <p>일단 몇가지 링크를 걸어봅니다.</p>  <p><a href="https://msdn.microsoft.com/en-US/vstudio/async" target="_blank">Async CTP 받기</a></p>  <p><a href="https://msdn.microsoft.com/en-us/magazine/hh463583.aspx" target="_blank">이번달 MSDN Magazine 보기</a></p>  <p>&#160;</p>  <p><a href="https://blogs.msdn.com/b/heejaechang/archive/2010/11/14/net-async.aspx" target="_blank">여기</a> 보시면 일단 여태까지의 .NET에서 밀었던 Async programming pattern의 변천사와 진화 과정을 보실수 있습니다. 이번 Async는 그 발전과정의 최종 혹은 가장 최신의 단계죠. 앞으로 또 바뀔수도 있겠지만, 현 단계에선 적어도 기존 패턴의 가장 큰 문제였던 람다 사용에 의한 로직의 복잡성을 획기적으로 없애줍니다. 물론 그렇다고 완벽하다고 할순 없고, 여전히 state 관리의 어려움은 남아 있죠. 하지만 단언코 현존하는 프로그램밍 방식중에는 가장 직관적이고 간단한 async 방법임에는 틀림없습니다.</p>  <p>&#160;</p>  <p>이 포스트는 여기까지 하고, 담 포스트는 이번 달 메가진에 나온 여러 async 글 들을 몇개 요약/번역해서 올리도록 하겠습니다.</p>  <p>&#160;</p>  <p>수고.</p>]]></content:encoded>
    </item>
    <item>
      <title>Reactive Extensions for .NET (Rx) 을 아시나요?</title>
      <link>https://docs.microsoft.com/archive/blogs/heejaechang/reactive-extensions-for-net-rx</link>
      <pubDate>Tue, 16 Nov 2010 03:23:26 GMT</pubDate>
      <dc:creator><![CDATA[HeeJae Chang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/heejaechang/2010/11/16/reactive-extensions-for-net-rx/</guid>
      <description><![CDATA[예전부터 한번 공부해 보고 싶던거 였는데 마침 channel9에 Rx에 대한 소개 비디오가 올라왔길래 봤는데 아주 쉽게 잘 설명해 놨더라구요. Rx가 뭔지.  아마 첨 보시면...]]></description>
      <content:encoded><![CDATA[<p>예전부터 한번 공부해 보고 싶던거 였는데 마침 channel9에 Rx에 대한 소개 비디오가 올라왔길래 봤는데 아주 쉽게 잘 설명해 놨더라구요. Rx가 뭔지.</p>  <p>아마 첨 보시면 어메메 하실지도 완전 짱입니다.</p>  <p>생각도 못해본 방식으로의 접근이네요. C# 5.0 에 들어가는 async/await랑은 또 다른 접근 방식인데. 그렇다고 둘이 배척되지 않고 같이 융합 될수 있겠는데요.</p>  <p>하여간 관심 있으면 함 봐 보세요.</p>  <p>그럼 수고.</p>  <h3><a href="https://channel9.msdn.com/posts/DC2010T0100-Keynote-Rx-curing-your-asynchronous-programming-blues" target="_blank">DevCamp 2010 Keynote - Rx: Curing your asynchronous programming blues</a></h3>]]></content:encoded>
    </item>
  </channel>
</rss>