<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>CLIEL LAB</title>
    <link>https://cliel.tistory.com/</link>
    <description>누구냐 넌?</description>
    <language>ko</language>
    <pubDate>Thu, 27 Aug 2026 09:29:58 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>클리엘</managingEditor>
    <image>
      <title>CLIEL LAB</title>
      <url>https://tistory1.daumcdn.net/tistory/3198881/attach/b54ab3ddabe146d0a8078b8317eed036</url>
      <link>https://cliel.tistory.com</link>
    </image>
    <item>
      <title>무차별 대입 공격 방어 도구 (Brute Force Attack Depend Tool) - Ban_Server</title>
      <link>https://cliel.tistory.com/entry/%EB%AC%B4%EC%B0%A8%EB%B3%84-%EB%8C%80%EC%9E%85-%EA%B3%B5%EA%B2%A9-%EB%B0%A9%EC%96%B4-%EB%8F%84%EA%B5%AC-Brute-Force-Attack-Depend-Tool-BanServer</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Remote Desktop의 경우 원격 port는 3389, MS-SQL Server의 경우 1433이 기본 port입니다. 그런데 server를 외부에서 접속 가능하도록 두면 이 2개 port에 대해서 무차별 대입 공격이 발생함을 알 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;For Remote Desktop, the default remote port is 3389, and for MS-SQL Server, it is 1433. However, if you leave the server accessible from outside, you can see that brute-force attacks occur on these two ports.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/puuo0/btrJ8JnwA0j/HaIdA5kkH0Qbw7FD6sDXBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/puuo0/btrJ8JnwA0j/HaIdA5kkH0Qbw7FD6sDXBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/puuo0/btrJ8JnwA0j/HaIdA5kkH0Qbw7FD6sDXBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpuuo0%2FbtrJ8JnwA0j%2FHaIdA5kkH0Qbw7FD6sDXBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;764&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;port번호를 바꾸면 일시적으로는 공격 빈도가 줄지만 시간이 지나면 다시 공격이 되살아나서 별 의미가 없습니다. 때문에 접속 가능한 IP만 제한적으로 열어두는 것이 제일 좋은데 만약 접속을 허용하는 IP만 특정할 수 없는 경우라면 해결 방법을 달리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Changing&amp;nbsp;the&amp;nbsp;port&amp;nbsp;number&amp;nbsp;may&amp;nbsp;temporarily&amp;nbsp;reduce&amp;nbsp;the&amp;nbsp;frequency&amp;nbsp;of&amp;nbsp;attacks,&amp;nbsp;but&amp;nbsp;over&amp;nbsp;time,&amp;nbsp;the&amp;nbsp;attacks&amp;nbsp;will&amp;nbsp;resume,&amp;nbsp;making&amp;nbsp;it&amp;nbsp;largely&amp;nbsp;ineffective.&amp;nbsp;Therefore,&amp;nbsp;the&amp;nbsp;best&amp;nbsp;approach&amp;nbsp;is&amp;nbsp;to&amp;nbsp;restrict&amp;nbsp;access&amp;nbsp;to&amp;nbsp;only&amp;nbsp;specific&amp;nbsp;IP&amp;nbsp;addresses.&amp;nbsp;If&amp;nbsp;it&amp;nbsp;is&amp;nbsp;not&amp;nbsp;possible&amp;nbsp;to&amp;nbsp;specify&amp;nbsp;the&amp;nbsp;exact&amp;nbsp;IP&amp;nbsp;addresses&amp;nbsp;allowed&amp;nbsp;for&amp;nbsp;access,&amp;nbsp;an&amp;nbsp;alternative&amp;nbsp;solution&amp;nbsp;must&amp;nbsp;be&amp;nbsp;implemented.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무차별 대입 공격에 대응할 수 있는 방법이야 여러가지로 존재할테지만 그 방법중의 하나로 Ban_Server와 Ban_MSSQL을 소개하고자 합니다. Ban_Server는 Windows Server에서 원격제어를, Ban_MSSQL은 MSSQL Server의 전용port로 무단 접속을 시도하면 이와 관련된 Log를 만들때 이를 감지하고 공격자의 IP를 차단하는 역활을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;There are various methods to counter indiscriminate brute-force attacks, and among them, we introduce Ban_Server and Ban_MSSQL. Ban_Server detects unauthorized attempts to remotely control Windows Server and Ban_MSSQL detects unauthorized attempts to connect to the dedicated port of MSSQL Server. When creating logs related to these attempts, they perform the role of detecting them and blocking the attacker's IP address.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 설치방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&amp;nbsp;Installation&amp;nbsp;Method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Microsoft의 아래 homepage에서 Console Application용으로 .NET 10 Runtime을 내려받아 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First,&amp;nbsp;download&amp;nbsp;and&amp;nbsp;install&amp;nbsp;the&amp;nbsp;.NET&amp;nbsp;10&amp;nbsp;Runtime&amp;nbsp;for&amp;nbsp;Console&amp;nbsp;Applications&amp;nbsp;from&amp;nbsp;the&amp;nbsp;Microsoft&amp;nbsp;homepage&amp;nbsp;below.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot;&gt;.NET 10.0 런타임 다운로드&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768523242457&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;.NET 10.0 런타임 다운로드&quot; data-og-description=&quot;.NET 10.0 런타임에는 기존 .NET 및 ASP.NET Core 애플리케이션을 실행하는 데 필요한 모든 것이 포함됩니다.&quot; data-og-host=&quot;dotnet.microsoft.com&quot; data-og-source-url=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot; data-og-url=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lLcGi/dJMb9dHhycy/5j1QmLiNJwEVSd170QlRq0/img.png?width=238&amp;amp;height=238&amp;amp;face=0_0_238_238,https://scrap.kakaocdn.net/dn/cuhpAM/dJMb8PGpFQa/eduHkMhhebEa7aFnfjkVD1/img.png?width=459&amp;amp;height=238&amp;amp;face=0_0_459_238&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lLcGi/dJMb9dHhycy/5j1QmLiNJwEVSd170QlRq0/img.png?width=238&amp;amp;height=238&amp;amp;face=0_0_238_238,https://scrap.kakaocdn.net/dn/cuhpAM/dJMb8PGpFQa/eduHkMhhebEa7aFnfjkVD1/img.png?width=459&amp;amp;height=238&amp;amp;face=0_0_459_238');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;.NET 10.0 런타임 다운로드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET 10.0 런타임에는 기존 .NET 및 ASP.NET Core 애플리케이션을 실행하는 데 필요한 모든 것이 포함됩니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dotnet.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;.NET 10 Runtime 설치를 하고 나면 아래 file을 내려 받고 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;file을 server의 임의폴더에 풀어줍니다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;After&amp;nbsp;installing&amp;nbsp;the&amp;nbsp;.NET&amp;nbsp;10&amp;nbsp;Runtime,&amp;nbsp;download&amp;nbsp;the&amp;nbsp;file&amp;nbsp;below&amp;nbsp;and&amp;nbsp;extract&amp;nbsp;it&amp;nbsp;to&amp;nbsp;any&amp;nbsp;folder&amp;nbsp;on&amp;nbsp;the&amp;nbsp;server.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/zE4MN/dJMcaivf7Wr/vKQ1KmuHqPF4427QwTFWSk/Ban.zip?attach=1&amp;amp;knm=tfile.zip&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;Ban.zip&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.40MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;압축을 풀고나면 Ban_Server와 Ban_MSSQL 2개의 folder가 있는지 확인하고 각각의 폴더를 원하는 위치로 옮겨줍니다. 만약 REMOTE와 MSSQL을 모두 사용할 필요가 없다면 필요한 것만 사용하고 다른건 삭제해도 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;After&amp;nbsp;unzipping,&amp;nbsp;verify&amp;nbsp;that&amp;nbsp;the&amp;nbsp;Ban_Server&amp;nbsp;and&amp;nbsp;Ban_MSSQL&amp;nbsp;folders&amp;nbsp;are&amp;nbsp;present,&amp;nbsp;then&amp;nbsp;move&amp;nbsp;each&amp;nbsp;folder&amp;nbsp;to&amp;nbsp;your&amp;nbsp;desired&amp;nbsp;location.&amp;nbsp;If&amp;nbsp;you&amp;nbsp;do&amp;nbsp;not&amp;nbsp;need&amp;nbsp;to&amp;nbsp;use&amp;nbsp;both&amp;nbsp;REMOTE&amp;nbsp;and&amp;nbsp;MSSQL,&amp;nbsp;you&amp;nbsp;may&amp;nbsp;use&amp;nbsp;only&amp;nbsp;the&amp;nbsp;necessary&amp;nbsp;one&amp;nbsp;and&amp;nbsp;delete&amp;nbsp;the&amp;nbsp;other.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이제 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;ask Scheduler를 등록해야 하는데 우선 &lt;/span&gt;Remote Desktop에 대응할 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Task Scheduler부터 등록합니다. 이를 위해&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Control Panel -&amp;gt; Administrative Tools -&amp;gt; Task Scheduler를 실행하여 아래 제시된 방법으로 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Task Scheduler를 등록합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;You&amp;nbsp;must&amp;nbsp;now&amp;nbsp;register&amp;nbsp;the&amp;nbsp;Scheduler.&amp;nbsp;Begin&amp;nbsp;by&amp;nbsp;registering&amp;nbsp;the&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;for&amp;nbsp;Remote&amp;nbsp;Desktop.&amp;nbsp;To&amp;nbsp;do&amp;nbsp;this,&amp;nbsp;run&amp;nbsp;Control&amp;nbsp;Panel&amp;nbsp;-&amp;gt;&amp;nbsp;Administrative&amp;nbsp;Tools&amp;nbsp;-&amp;gt;&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;and&amp;nbsp;register&amp;nbsp;the&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;using&amp;nbsp;the&amp;nbsp;method&amp;nbsp;described&amp;nbsp;below.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rLbVG/btrJ8HXvuec/AERiDLuX7oDTctUcS9AcNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rLbVG/btrJ8HXvuec/AERiDLuX7oDTctUcS9AcNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rLbVG/btrJ8HXvuec/AERiDLuX7oDTctUcS9AcNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrLbVG%2FbtrJ8HXvuec%2FAERiDLuX7oDTctUcS9AcNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;오른쪽의 Create Task를 선택합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Select&amp;nbsp;Create&amp;nbsp;Task&amp;nbsp;on&amp;nbsp;the&amp;nbsp;right.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EWjMA/btrJ8rfmJGM/9zvopuMy3RdaSxgw9fcid1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EWjMA/btrJ8rfmJGM/9zvopuMy3RdaSxgw9fcid1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EWjMA/btrJ8rfmJGM/9zvopuMy3RdaSxgw9fcid1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEWjMA%2FbtrJ8rfmJGM%2F9zvopuMy3RdaSxgw9fcid1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Name에 Ban_REMOTE를 입력하고 아래 Security Options에는 'Run whether user is logged on or not'을 선택합니다. 이때 Name은 해당&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Task Scheduler가 어떠한 목적으로 만들어졌는지를 구분할 수 있을 정도면 다른 이름을 지정할 수도 있습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Enter&amp;nbsp;Ban_REMOTE&amp;nbsp;in&amp;nbsp;the&amp;nbsp;Name&amp;nbsp;field&amp;nbsp;and&amp;nbsp;select&amp;nbsp;&amp;lsquo;Run&amp;nbsp;whether&amp;nbsp;user&amp;nbsp;is&amp;nbsp;logged&amp;nbsp;on&amp;nbsp;or&amp;nbsp;not&amp;rsquo;&amp;nbsp;under&amp;nbsp;Security&amp;nbsp;Options.&amp;nbsp;At&amp;nbsp;this&amp;nbsp;point,&amp;nbsp;you&amp;nbsp;can&amp;nbsp;assign&amp;nbsp;a&amp;nbsp;different&amp;nbsp;name&amp;nbsp;to&amp;nbsp;the&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;as&amp;nbsp;long&amp;nbsp;as&amp;nbsp;it&amp;nbsp;clearly&amp;nbsp;indicates&amp;nbsp;the&amp;nbsp;purpose&amp;nbsp;for&amp;nbsp;which&amp;nbsp;it&amp;nbsp;was&amp;nbsp;created.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1Fhzd/btrKcwm9DON/9jJ1NAH7KyPT0bB6dbk6TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1Fhzd/btrKcwm9DON/9jJ1NAH7KyPT0bB6dbk6TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1Fhzd/btrKcwm9DON/9jJ1NAH7KyPT0bB6dbk6TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1Fhzd%2FbtrKcwm9DON%2F9jJ1NAH7KyPT0bB6dbk6TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;480&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 Trigger Tab으로 이동해 Begin the task에 'On an event'를 선택하고 Log에는 Security를 Event ID로는 4625를 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next,&amp;nbsp;go&amp;nbsp;to&amp;nbsp;the&amp;nbsp;Trigger&amp;nbsp;tab,&amp;nbsp;select&amp;nbsp;&amp;lsquo;On&amp;nbsp;an&amp;nbsp;event&amp;rsquo;&amp;nbsp;for&amp;nbsp;Begin&amp;nbsp;the&amp;nbsp;task,&amp;nbsp;and&amp;nbsp;enter&amp;nbsp;Security&amp;nbsp;for&amp;nbsp;Log&amp;nbsp;and&amp;nbsp;4625&amp;nbsp;for&amp;nbsp;Event&amp;nbsp;ID.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wkHqc/btrKeiIrNqA/NWQeYXStVgKdlCgZX0MKgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wkHqc/btrKeiIrNqA/NWQeYXStVgKdlCgZX0MKgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wkHqc/btrKeiIrNqA/NWQeYXStVgKdlCgZX0MKgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwkHqc%2FbtrKeiIrNqA%2FNWQeYXStVgKdlCgZX0MKgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;516&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actions tab에서는 Action에 'Start a program'을 선택하고 Program/script에 Ban_Server.exe를 명시한 뒤 Start in (optional)에 Ban_Server.exe가 존재하는 folder를 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In&amp;nbsp;the&amp;nbsp;Actions&amp;nbsp;tab,&amp;nbsp;select&amp;nbsp;&amp;lsquo;Start&amp;nbsp;a&amp;nbsp;program&amp;rsquo;&amp;nbsp;for&amp;nbsp;Action,&amp;nbsp;specify&amp;nbsp;Ban_Server.exe&amp;nbsp;in&amp;nbsp;Program/script,&amp;nbsp;and&amp;nbsp;then&amp;nbsp;designate&amp;nbsp;the&amp;nbsp;folder&amp;nbsp;where&amp;nbsp;Ban_Server.exe&amp;nbsp;is&amp;nbsp;located&amp;nbsp;in&amp;nbsp;Start&amp;nbsp;in&amp;nbsp;(optional).&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qvxYd/btrKbsFkaOI/xizlHdfkeyudrmin1Cxkkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qvxYd/btrKbsFkaOI/xizlHdfkeyudrmin1Cxkkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qvxYd/btrKbsFkaOI/xizlHdfkeyudrmin1Cxkkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqvxYd%2FbtrKbsFkaOI%2FxizlHdfkeyudrmin1Cxkkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;500&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것으로 REMOTE(Ban_Server)에 대한&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Task Scheduler을 완료하였습니다. 같은 방법으로 MSSQL에 관한 event로 등록해 줘야 하는데 위의 설정과정 대부분이 동일합니다. 다만 MSSQL의 경우 Trigger에서 Log를 Application을 잡고 Event ID를 18456으로 해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;This&amp;nbsp;completes&amp;nbsp;the&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;setup&amp;nbsp;for&amp;nbsp;REMOTE(Ban_Server).&amp;nbsp;You&amp;nbsp;must&amp;nbsp;register&amp;nbsp;the&amp;nbsp;event&amp;nbsp;for&amp;nbsp;MSSQL&amp;nbsp;using&amp;nbsp;the&amp;nbsp;same&amp;nbsp;method;&amp;nbsp;most&amp;nbsp;of&amp;nbsp;the&amp;nbsp;configuration&amp;nbsp;steps&amp;nbsp;above&amp;nbsp;are&amp;nbsp;identical.&amp;nbsp;However,&amp;nbsp;for&amp;nbsp;MSSQL,&amp;nbsp;you&amp;nbsp;must&amp;nbsp;set&amp;nbsp;the&amp;nbsp;Application&amp;nbsp;in&amp;nbsp;the&amp;nbsp;Trigger&amp;nbsp;to&amp;nbsp;Log&amp;nbsp;and&amp;nbsp;the&amp;nbsp;Event&amp;nbsp;ID&amp;nbsp;to&amp;nbsp;18456.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;2. 사용방법&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;2.&amp;nbsp;How&amp;nbsp;to&amp;nbsp;Use&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;압축을 풀어놓은 folder를 보면 jsconfig.json이라는 이름의 file을 볼 수 있습니다. 해당 file을 아래 내용으로 구성되어 있습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;When you unzip the folder, you will find a file named jsconfig.json. This file is structured as follows:&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;{ &lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;FailedCount&quot;: 1, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;WhiteLists&quot;:&amp;nbsp;[] &lt;br /&gt;}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FailedCount는 실패한 건수에 대한 대응입니다. 동일한 IP에 대하여 설정한 횟수 이상의 접속실패건이 발생하는 경우에만 해당 IP를 방화벽에 등록하여 차단을 수행하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FailedCount&amp;nbsp;corresponds&amp;nbsp;to&amp;nbsp;the&amp;nbsp;number&amp;nbsp;of&amp;nbsp;failures.&amp;nbsp;Only&amp;nbsp;when&amp;nbsp;the&amp;nbsp;number&amp;nbsp;of&amp;nbsp;connection&amp;nbsp;failures&amp;nbsp;exceeds&amp;nbsp;the&amp;nbsp;set&amp;nbsp;limit&amp;nbsp;for&amp;nbsp;the&amp;nbsp;same&amp;nbsp;IP&amp;nbsp;address&amp;nbsp;will&amp;nbsp;that&amp;nbsp;IP&amp;nbsp;be&amp;nbsp;registered&amp;nbsp;in&amp;nbsp;the&amp;nbsp;firewall&amp;nbsp;and&amp;nbsp;blocked.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WhiteLists는 Ban_Server.exe가 예외로 둘 IP를 지정하는 곳입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WhiteLists&amp;nbsp;is&amp;nbsp;where&amp;nbsp;Ban_Server.exe&amp;nbsp;specifies&amp;nbsp;the&amp;nbsp;IP&amp;nbsp;addresses&amp;nbsp;to&amp;nbsp;be&amp;nbsp;treated&amp;nbsp;as&amp;nbsp;exceptions.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;FailedCount&quot;: 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;WhiteLists&quot;: [&quot;192.168.10.1&quot;, &quot;192.168.10.2&quot;]&lt;br /&gt;}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 만약 위와 같이 '192.168.10.1'과 '192.168.10.2'의 IP를 등록했다면 해당 IP에 대해서만 접속실패가 발생하더라도 차단방화벽의 IP로 등록하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore,&amp;nbsp;if&amp;nbsp;you&amp;nbsp;have&amp;nbsp;registered&amp;nbsp;the&amp;nbsp;IP&amp;nbsp;addresses&amp;nbsp;&amp;lsquo;192.168.10.1&amp;rsquo;&amp;nbsp;and&amp;nbsp;&amp;lsquo;192.168.10.2&amp;rsquo;&amp;nbsp;as&amp;nbsp;described&amp;nbsp;above,&amp;nbsp;even&amp;nbsp;if&amp;nbsp;access&amp;nbsp;failures&amp;nbsp;occur&amp;nbsp;only&amp;nbsp;for&amp;nbsp;those&amp;nbsp;specific&amp;nbsp;IPs,&amp;nbsp;they&amp;nbsp;will&amp;nbsp;not&amp;nbsp;be&amp;nbsp;added&amp;nbsp;to&amp;nbsp;the&amp;nbsp;firewall's&amp;nbsp;blocked&amp;nbsp;list.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 기타&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.&amp;nbsp;Other&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Ban_Server는 Open source project이며 Git을 통해 source를 살펴보고 변경할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Ban_Server&amp;nbsp;is&amp;nbsp;an&amp;nbsp;open-source&amp;nbsp;project,&amp;nbsp;and&amp;nbsp;you&amp;nbsp;can&amp;nbsp;view&amp;nbsp;and&amp;nbsp;modify&amp;nbsp;the&amp;nbsp;source&amp;nbsp;code&amp;nbsp;via&amp;nbsp;Git.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/cliel/Ban_Server&quot;&gt;cliel/Ban_Server: 무차별 대입 공격 방어(Brute Force Attack Defend) (github.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661113307982&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - cliel/Ban_Server: 무차별 대입 공격 방어(Brute Force Attack Defend)&quot; data-og-description=&quot;무차별 대입 공격 방어(Brute Force Attack Defend). Contribute to cliel/Ban_Server development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/cliel/Ban_Server&quot; data-og-url=&quot;https://github.com/cliel/Ban_Server&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kaKeV/hyPv0QLp7d/8bWdsYsBQ4Rq4uHJkgLAFk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/cliel/Ban_Server&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/cliel/Ban_Server&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kaKeV/hyPv0QLp7d/8bWdsYsBQ4Rq4uHJkgLAFk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - cliel/Ban_Server: 무차별 대입 공격 방어(Brute Force Attack Defend)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;무차별 대입 공격 방어(Brute Force Attack Defend). Contribute to cliel/Ban_Server development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;※ 해당 프로그램 사용으로 인한 부작용에 대해서는 책임지지 않습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;※&amp;nbsp;We&amp;nbsp;are&amp;nbsp;not&amp;nbsp;responsible&amp;nbsp;for&amp;nbsp;any&amp;nbsp;adverse&amp;nbsp;effects&amp;nbsp;resulting&amp;nbsp;from&amp;nbsp;the&amp;nbsp;use&amp;nbsp;of&amp;nbsp;this&amp;nbsp;program.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ json에서 FailedCount는 정확성을 보장하지 않습니다. FailCount를 3으로 하면 &quot;3번 실패 시 작동한다.&quot;가 아니라 &quot;3번 이상 실패 시 작동한다&quot;쯤으로 해석해야 합니다. LOG를 읽고 program이 동작하는 시점에 다시 공격이 들어올 수 있는 등 여러 가지 예외 사항이 발생하므로 IP가 Block 완료될 때쯤 실제 공격은 3회 이상으로 이루어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※&amp;nbsp;The&amp;nbsp;FailedCount&amp;nbsp;in&amp;nbsp;JSON&amp;nbsp;does&amp;nbsp;not&amp;nbsp;guarantee&amp;nbsp;accuracy.&amp;nbsp;Setting&amp;nbsp;FailCount&amp;nbsp;to&amp;nbsp;3&amp;nbsp;should&amp;nbsp;be&amp;nbsp;interpreted&amp;nbsp;as&amp;nbsp;&amp;ldquo;activates&amp;nbsp;after&amp;nbsp;3&amp;nbsp;or&amp;nbsp;more&amp;nbsp;failures,&amp;rdquo;&amp;nbsp;not&amp;nbsp;&amp;ldquo;activates&amp;nbsp;after&amp;nbsp;exactly&amp;nbsp;3&amp;nbsp;failures.&amp;rdquo;&amp;nbsp;Various&amp;nbsp;exceptions&amp;nbsp;can&amp;nbsp;occur,&amp;nbsp;such&amp;nbsp;as&amp;nbsp;attacks&amp;nbsp;resuming&amp;nbsp;while&amp;nbsp;the&amp;nbsp;program&amp;nbsp;is&amp;nbsp;processing&amp;nbsp;logs.&amp;nbsp;Therefore,&amp;nbsp;by&amp;nbsp;the&amp;nbsp;time&amp;nbsp;the&amp;nbsp;IP&amp;nbsp;is&amp;nbsp;fully&amp;nbsp;blocked,&amp;nbsp;the&amp;nbsp;actual&amp;nbsp;attack&amp;nbsp;may&amp;nbsp;have&amp;nbsp;occurred&amp;nbsp;more&amp;nbsp;than&amp;nbsp;three&amp;nbsp;times.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;※ Ban_Server는 Windows Server상의 EventLog를 기반으로 동작합니다. Log가 많이 쌓아두면 그 만큼 처리하는데 시간이 오래 걸릴 수 있으며 Log의 관리특성에 따라 원하는 방향으로 프로그램이 동작하지 않을 수 있기 때문에 적절한 관리가 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;※&amp;nbsp;Ban_Server&amp;nbsp;operates&amp;nbsp;based&amp;nbsp;on&amp;nbsp;the&amp;nbsp;EventLog&amp;nbsp;on&amp;nbsp;Windows&amp;nbsp;Server.&amp;nbsp;If&amp;nbsp;logs&amp;nbsp;accumulate&amp;nbsp;excessively,&amp;nbsp;processing&amp;nbsp;may&amp;nbsp;take&amp;nbsp;longer,&amp;nbsp;and&amp;nbsp;depending&amp;nbsp;on&amp;nbsp;log&amp;nbsp;management&amp;nbsp;characteristics,&amp;nbsp;the&amp;nbsp;program&amp;nbsp;may&amp;nbsp;not&amp;nbsp;function&amp;nbsp;as&amp;nbsp;intended.&amp;nbsp;Therefore,&amp;nbsp;proper&amp;nbsp;management&amp;nbsp;is&amp;nbsp;required.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Server/Windows Server</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/534</guid>
      <comments>https://cliel.tistory.com/entry/%EB%AC%B4%EC%B0%A8%EB%B3%84-%EB%8C%80%EC%9E%85-%EA%B3%B5%EA%B2%A9-%EB%B0%A9%EC%96%B4-%EB%8F%84%EA%B5%AC-Brute-Force-Attack-Depend-Tool-BanServer#entry534comment</comments>
      <pubDate>Fri, 16 Jan 2026 09:48:42 +0900</pubDate>
    </item>
    <item>
      <title>Hyper-V Virtual Machine상에서 Docker실행하기</title>
      <link>https://cliel.tistory.com/entry/Hyper-V-Virtual-Machine%EC%83%81%EC%97%90%EC%84%9C-Docker%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;일단 Virtual Machine상에서 Docker를 설치할 때 설치 option에서 Hyper-V대신 WSL(Windows Subsystem Linux)을 사용한다는 설정이 있습니다. 이 설정을 제외하고 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 뒤 Docker를 실행하면 다음과 같은 오류를 볼 수 있는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;721&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jnsyv/btsHaVRwgN9/NIkEqGRkNkULxYGywXqypk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jnsyv/btsHaVRwgN9/NIkEqGRkNkULxYGywXqypk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jnsyv/btsHaVRwgN9/NIkEqGRkNkULxYGywXqypk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJnsyv%2FbtsHaVRwgN9%2FNIkEqGRkNkULxYGywXqypk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1270&quot; height=&quot;721&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;721&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말이 길지만 내용은 BIOS상에서 가상화를 설정해야 한다는 것입니다. 하지만 Hyper-V의 Virtual Machine에서 BIOS를 제한적일 수밖에 없습니다. 이 문제를 해결하려면 Virtual Machine를 끈뒤 Host Machine상에서 관리자 권한으로 PowerShell를 실행하고 아래 명령을 통해 Virtual Machine의 가상화를 설정해 줘야 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Set-VMProcessor&amp;nbsp;-VMName&amp;nbsp;Windows11&amp;nbsp;-ExposeVirtualizationExtensions&amp;nbsp;$true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 명령에서 Windows11은 Virtual Machine의 이름입니다. Virtual Machine의 이름에 'Windows 11'처럼 공백이 있으면 오류가 날 수 있으므로 공백을 없애주는 것이 좋습니다.&lt;/blockquote&gt;</description>
      <category>ETC</category>
      <category>docker</category>
      <category>hyper-v</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/613</guid>
      <comments>https://cliel.tistory.com/entry/Hyper-V-Virtual-Machine%EC%83%81%EC%97%90%EC%84%9C-Docker%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0#entry613comment</comments>
      <pubDate>Sat, 4 May 2024 16:53:25 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 11. LINQ</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-11-LINQ</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ(Language INtegrated Query)는 일련의 data를 대상으로 filtering 및 sorting 하고 다른 형태로 결과를 투영할 수 있는 언어확장 도구입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 왜 LINQ인가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 명령형및 선언형 언어의 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ는 2008년 .NET 3.0과 .NET Framework 3.0과 함께 도입되었습니다. 그전에 C#및 .NET개발자는 명령형이라고 하는 절차적 code문을 사용해 예를 들어 loop처럼 일련의 item들을 처리하곤 했습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;첫 번째 item에 대한 현재 위치를 설정합니다.&lt;/li&gt;
&lt;li&gt;지정한 값과 하나 또는 그 이상의 속성을 비교 비교하여 예를 들어 가격이 50 이상이어야 한다거나 수량이 동일한지등과 같은 경우처럼 처리해야 하는 item인지를 확인합니다.&lt;/li&gt;
&lt;li&gt;2번에서 조건과 일치하는 경우라면 item을 처리합니다. 예를 들어 사용자에게 하나 또는 그 이상의 속성을 표시하거나 새로운 값으로 update 할 수 있고 또는 item을 제거하거나 counting과 summing처럼 값을 집계할 수도 있을 것입니다.&lt;/li&gt;
&lt;li&gt;다음 item으로 이동합니다. 모든 item이 처리될때까지 이 과정을 반복합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령형이라고 하는 절차적 code는 compiler에게 '이렇게 하라. 저렇게 하라'와 같이 주어진 목표를 어떻게 달성할 수 있는지를 말해줍니다. Compiler는 우리가 달성하고자 하는 목표자체를 알 수 없기 때문에 그 자체로 우리를 도울 수는 없습니다. 모든 방법적 절차가 정확하게 맞다는 것에 대한 모든 책임은 100% 개발자가에게 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ는 이러한 일반적인 작업들을 오류를 최소화하면서 더 쉽게 code를 만들 수 있도록 해줍니다. Move나 read, update 기타 등등 각각의 동작에 대한 상태를 명시적으로 설명하는 대신 LINQ는 개발자가 선언적 혹은 기능적인 style의 문을 작성할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언적 code는 compiler에게 달성하기 위한 목표가 무엇인지를 말해주며 compiler는 가장 최적의 방법을 통해 목표를 달성하게 됩니다. 또한 해당 문은 기존보다 더 간소하면서도 깔끔한 상태를 유지할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;LINQ를 제대로 이해하지 않으면 작성한 code는 약간의 자체적인 bug를 갖게 될 수 있습니다. 아래 글을 읽어보세요.(&lt;a href=&quot;https://twitter.com/amantinband/status/1559187912218099714&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://twitter.com/amantinband/status/1559187912218099714&lt;/a&gt;). 해당 부분과 관련해서 솔직히 말하자면 이것은 LINQ의 동작과 multi-threading 동작이 결합된 것으로 사람들이 가장 혼란스러워 하는 것이 이 부분이므로 code가 위험해 질 수 있는 부분을 잘 이해해야 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. LINQ 표현식&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글을 통해 우리는 이미 간단하게나마 LINQ를 사용해 본적이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 위의 글에 대한 핵심은 LINQ가 아니므로 LINQ가 어떻게 동작하는지에 대해서는 생략했습니다. 이제 LINQ의 표현식을 이해하기 위한 시간을 가져보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 무엇이 LINQ를 만드는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ는 몇가지 부분으로 나뉠 수 있는데 일부는 필수적이지만 일부는 선택적입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Extension method(필수적) : 여기에는 Where나 OrderBy 그리고 Select를 포함한 LINQ의 기능을 제공합니다.&lt;/li&gt;
&lt;li&gt;LINQ provider(필수적) : 여기에는 memory상에서 개체를 표현하기 위한 LINQ to Objects와 외부 database에 저장된 data와 EF Core를 통한 model을 처리하기 위한 LINQ to Entities, 그리고 XML로 저장된 data를 처리하기 위한 LINQ to XML을 포함합니다. 이들 provider는 다양한 유형의 data에 대해 특정한 방식으로 LINQ표현식을 실행합니다.&lt;/li&gt;
&lt;li&gt;Lambda 표현식(선택적) : LINQ query를 간소화하기 위해 명명된 method대신 예를 들어 filtering를 위한 Where method를 조건부 논리와 같이 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;LINQ query comprehension 구문(선택적) : 여기에는 from, in, where, orderby, descending, select와 같은 C# keyword가 존재합니다. 이들은 몇몇 LINQ 표현 method에 대한 별칭에 해당하며 특히 이미 SQL(Structured Query Language)과 같이 다른 query언어를 경험해 본 적이 있다면 이들을 더욱 친숙하게 사용할 수 있을 것입니다. 처음 LINQ를 사용할 때는 종종 LINQ query comprehension 구문을 LINQ라고 오해하는 경우도 있는데 이것은 선택적으로 사용하는 LINQ의 여러 부분 중 하나에 해당할 뿐입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 열거 가능한 class에 LINQ 표현식 적용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Where나 Select와 같은 LINQ 확장 method는 Enumerable static class에 의해 IEnumerable&amp;lt;T&amp;gt;를 구현하는 모든 sequence type에 추가되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 모든 type의 array는 IEnumerable&amp;lt;T&amp;gt; class를 구현하며 여기서 T는 array에 속하는 item에 대한 type을 의미합니다. 다시 말해 모든 array는 질의와 이들의 조작하는데 대한 LINQ를 지원한다고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;T&amp;gt;, Dictionary&amp;lt;TKey, TValue&amp;gt;, Stack&amp;lt;T&amp;gt;, Queue&amp;lt;T&amp;gt;와 같은 모든 generic collection은 IEnumerable&amp;lt;T&amp;gt;를 구현하므로 이들도 역시 LINQ를 통해 data질의하고 변경할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enumerable은 아래 표에서 요약된것처럼 50여 개 이상의 method를 정의하고 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 표는 향후 참고용으로 유용할 수 있지만 지금은 간단하게 어떤 method가 존재하고 나중에 어떤 method를 사용하는 것이 적절한지 확인하는 차원에서 살펴볼 수 있습니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Method&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;First, FirstOrDefault, Last, LastOrDefault&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Sequence에서 첫번째 혹은 마지막 item을 가져오며 예외 혹은 type에 대한 기본값을 반환할 수 있습니다. 예를 들어 첫번째 혹은 마지막 item이 존재하지 않는 경우 int type이면 0을 참조 type이라면 null을 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Where&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정한 filter와 일치하는 item에 대한 sequence를 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Single, SingleOrDefault&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정한 filter와 일치하는 item을 반환하거나 아니면 예외 혹은 일치하는 것이 존재하지 않는다면 type에 대한 기본값을 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;ElementAt, ElementAtOrDefault&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정한 index위치의 item을 반환하거나 아니면 예외 혹은 position에 해당하는 item이 존재하지 않는 경우 type의 기본값을 반환합니다. .NET 6에서 도입된것은 int대신 Index를 전달할 수 있게 overload되어 Span&amp;lt;T&amp;gt; sequences에서 더 효휼적입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Select, SelectMany&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item을 다른 type으로 투영하며 item의 중첩된 계층을 평탄화합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;OrderBy, OrderByDescending, ThenBy, ThenByDescending&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정된 field또는 속성을 통해 item을 정렬합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Order, OrderDescending&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;.NET 7에 도입된 것으로 item을 item자체별로 정렬합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Reverse&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item의 순서를 뒤집습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;GroupBy, GroupJoin, Join&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;두개의 sequences를 group화 하거나 join합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Skip, SkipWhile&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item 특정 수만큼 skip하거나 표현식이 true일때까지 skip합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Take, TakeWhile&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item을 특정 수만큼 가져오거나 표현식이 true일땨까지 가져옵니다. .NET 6에서 도입된 것은 Range를 전달할 수 있도록 overload되었는데 예를 들어 Take(range: 3..^5)은 3번째 item부터 시작하여 5번째 item에서 끝나는 부분집합을 가져온다는 것을 의미합니다. 또는 Skip(4)대신 Take(4..)와 같이 사용할 수도 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Aggregate, Average, Count, LongCount, Max, Min, Sum&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;전체값 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;TryGetNonEnumeratedCount&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Count()는 Count속성이 sequence하에서 구현되었는지를 확인하고 그 값을 반환하거나 item을 count하기 위해 전체 sequence를 열거합니다. .NET 6에서 도입된 것은 해당 method로 오로지 Count만을 확인하여 없는 경우 잠재적인 저성능 운용을 동작을 방지하기 위해 false를 반환하며 out매개변수를 0으로 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;All, Any, Contains&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item이 filter에 대해 전체 혹은 일부와 일치하거나 sequence가 지정한 item을 포함하고 있다면 true를 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Cast&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정한 type으로 item을 형변환합니다. 이것은 비 generic개체를 generic type으로 변환할때 유용하게 사용될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Distinct&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;중복 item을 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;DistinctBy, ExceptBy, IntersectBy, UnionBy, MinBy, MaxBy&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item의 전체가 아닌 일부만을 사용해 비교를 수행할 수 있습니다. 예를 들어 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;전체&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Person개체를 비교함으로서 Distinct를 통해 중복 item을 제거하는 대신 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;LastName과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;DateOfBirth만 비교함으로서 DistinctBy를 통해 중복 item을 제거할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Chunk&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;크기가 지정된 batch로 sequence를 분할합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Append, Concat, Prepend&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Sequence별 결합동작을 수행합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Zip&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Items의 위치를 기반으로 2개 혹은 3개의 sequence에 대한 일치 작업을 수행합니다. 예를 들어 첫번째 sequence에서 position이 1인 item은 두번째 sequence의 position 1인 item과 일치합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;ToArray, ToList, ToDictionary, ToHashSet, ToLookup&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Sequence를 array나 collection으로 변환합니다. 이들은 지연된 실행을 기다라기 보다는 LINQ표현식의 즉각적인 실행을 강제하는 유일한 method입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;As와 To로 시작하는 확장 method의 차이에 대해 확실히 해둘 필요가 있습니다. As로 시작하는, 예를 들어 AsEnumerable method는 sequence를 다른 type으로 변환하기는 하지만 memory를 할당하지 않으므로 빠르게 동작합니다. 하지만 To로 시작하는, 예를 들어 ToList와 같은 method는 새로운 item의 sequence에 대한 memory를 할당하게 되므로 상대적으로 느릴 수 있으며 또한 더 많은 memory resource를 사용하게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enumerable class는 또한 확장 method가 아닌 아래 표에서 설명한 바와 같이 다른 몇몇 method들도 가지고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 111px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.9535%; height: 17px;&quot;&gt;Method&lt;/td&gt;
&lt;td style=&quot;width: 86.0465%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 60px;&quot;&gt;
&lt;td style=&quot;width: 13.9535%; height: 60px;&quot;&gt;Empty&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.0465%; height: 60px;&quot;&gt;지정된 type T에 대한 빈 sequence를 반환합니다. 대게 IEnumerable&amp;lt;T&amp;gt;를 필요로 하는 method에서 빈 sequence를 전달할때 유용하게 사용될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.9535%; height: 17px;&quot;&gt;Range&lt;/td&gt;
&lt;td style=&quot;width: 86.0465%; height: 17px;&quot;&gt;Count item을 통한 시작값으로 부터 정수 sequence를 반환합니다. 예를 들어 Enumerable.Range(start: 5, count: 3)는 정수 5, 6 그리고 7을 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.9535%; height: 17px;&quot;&gt;Repeat&lt;/td&gt;
&lt;td style=&quot;width: 86.0465%; height: 17px;&quot;&gt;Count횟수만큼 반복되는 같은 요소를 포함하는 sequence를 반환합니다. 예를 들어 Enumerable.Repeat(element: &quot;5&quot;, count: 3)은 문자열값 &quot;5&quot;, &quot;5&quot;, &quot;5&quot;를 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;(3) 지연된 실행(deferred execution)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ는 지연된 실행을 사용합니다. 이는 위에서 언급한 확장 method 대부분을 호출하면 query를 실행하지 않고 결과를 가져온다는 것을 이해하는데 중요한 부분이며 이때 이들 확장 method의 대부분은 답이 아닌 질문을 나타내는 LINQ 표현식을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022에서 csStudy11 solution을 생성하고 여기에 LinqWithObjects이름의 console app project를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Program.cs에서 기존의 문을 모두 제거하고 아래와 같이 특정 인물에 대한 문자열값의 sequence를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691048680529&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] names = new[] { &quot;이순신&quot;, &quot;이도&quot;, &quot;신사임당&quot;, &quot;홍길동&quot;, &quot;김종서&quot;, &quot;박혁거세&quot;, &quot;김좌진&quot;, &quot;윤봉길&quot; };

// 질문 : 이름이 '이'자로 시작하는 사람은 누구인가?

//확장 method 사용
var query1 = names.Where(name =&amp;gt; name.StartsWith(&quot;이&quot;));

//LINQ query comprehension syntax 사용
var query2 = from name in names where name.StartsWith(&quot;이&quot;) select name;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문에 대한 답을 얻으려면 즉, query를 실행하려면 다음과 같이 ToArray나 ToLookup과 같은 To method 중 하나를 호출하거나 query를 열거해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691049178009&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//질문의 답을 포함하는 문자열 arrary를 반환
string[] result1 = query1.ToArray();
//질문의 답을 포함하는 문자열의 list를 반환
List&amp;lt;string&amp;gt; result2 = query2.ToList();
//결과를 열거함으로서 질문의 답을 반환
foreach (string name in query1)
{
	Console.WriteLine(name);

	names[1] = &quot;유관순&quot;; //이도를 유관순으로 변경 -&amp;gt; 유관순은 '이'자로 시작하지 않는다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;77&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8HTZ2/btsFEw4QxVN/BaRQ21krNiMzSxJaFGHp91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8HTZ2/btsFEw4QxVN/BaRQ21krNiMzSxJaFGHp91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8HTZ2/btsFEw4QxVN/BaRQ21krNiMzSxJaFGHp91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8HTZ2%2FbtsFEw4QxVN%2FBaRQ21krNiMzSxJaFGHp91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;77&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;77&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연된 실행때문에 첫 번째 결과인 '이순신'을 출력한 이후 본래 배열의 값이 바뀌게 되면 loop로 다시 돌아갈 때쯤 '이도'가 '유관순'으로 변경되었으므로 더 이상 일치하는 것이 없게 되고 따라서 오로지 '이순신'만 표시되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Where를 통한 entity filtering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ를 사용하는 가장 일반적인 이유는 Where확장 method를 사용해 sequence안에서 item을 filtering하기 위한 것입니다. 예제를 통해 sequence를 정의하고 여기에 LINQ동작을 적용함으로써 filtering을 사용해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Project file(*.csproj)에서 아래와 같이 global로 자동으로 import 되는 System.Linq를 제거하기 위한 요소를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691115147839&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
	&amp;lt;Using Remove=&quot;System.Linq&quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 names에 Where 확장 method를 호출하도록 합니다. 이때 Where를 입력하기 위해 typing울 시도하면 아래와 같이 IntelliSense에 의해 array에서 사용가능한 member들이 표시되는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4ahAJ/btsFDB6EMih/1EATk7ZqUVzze5AO1lxHP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4ahAJ/btsFDB6EMih/1EATk7ZqUVzze5AO1lxHP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4ahAJ/btsFDB6EMih/1EATk7ZqUVzze5AO1lxHP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4ahAJ%2FbtsFDB6EMih%2F1EATk7ZqUVzze5AO1lxHP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;859&quot; height=&quot;302&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상한 점은 여기에 표시되는 member들 중에서는 Where가 존재하지 않는다는 것입니다. 이는 Where가 확장 method이기 때문이며 array type에는 기본적으로 존재하지 않는 member이기 때문입니다. 따라서 Where 확장 method를 사용하려면 System.Linq namespace를 import 해야 합니다. .NET 6부터는 암시적으로 import 되는 것이 기본이지만 위에서 우리는 이미 System.Linq가 import 되지 않도록 Remove 했기 때문에 사용할 수 없게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Project file에서 위에서 추가한 System.Linq Remove요소를 삭제하고 names에서 Where method를 호출하도록 시도하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w9IT2/btsFDDi6kmg/E4Zn3ykZEVTqsJVsqpZl51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w9IT2/btsFDDi6kmg/E4Zn3ykZEVTqsJVsqpZl51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w9IT2/btsFDDi6kmg/E4Zn3ykZEVTqsJVsqpZl51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw9IT2%2FbtsFDDi6kmg%2FE4Zn3ykZEVTqsJVsqpZl51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;949&quot; height=&quot;189&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntelliSense는 Enumerable class에 의해 추가된 extension method를 추가한 member들을 표시할 것입니다. 또한&amp;nbsp;Where method에서 IntelliSense는 Where method를 호출하기 위해 Func&amp;lt;string, bool&amp;gt; delegate의 instance를 전달해야 함을 말해주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Func&amp;lt;string, bool&amp;gt; delegate의 새로운 instance를 생성하기 위한 표현식을 입력합니다. 하지만 다음 단계에서 method의 이름을 정의할 것이기 때문에 아직까지는 method이름을 제공하지 않았습니다. (오류발생)&lt;/p&gt;
&lt;pre id=&quot;code_1691116238908&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names.Where(new Func&amp;lt;string, bool&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Func&amp;lt;string, bool&amp;gt; delegate는 우리에게 각 string변수에 method로 전달되고 method는 반드시 bool값을 반환해야 함을 말해주고 있습니다. Method가 true를 반환한다면 이것은 결과에서 해당 string을 포함하고 있음을 의미하며 그렇지 않으면 포함하고 있지 않음을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 명명된 method 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project에서 Function.cs 이름의 class file을 추가하고 여기에 partial Program class를 정의한 뒤 아래와 같이 3글자 이상의 이름만을 포함시킬 method를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691136823919&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	static bool NameLongerThanFour(string name)
	{
		return name.Length &amp;gt; 3;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs로 돌아와 위에서 정의했던 Func&amp;lt;string, bool&amp;gt; delegate로 method의 이름을 전달하고&lt;/p&gt;
&lt;pre id=&quot;code_1691136905230&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names.Where(new Func&amp;lt;string, bool&amp;gt;(NameLongerThanFour));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 query를 열거하도록 하면&lt;/p&gt;
&lt;pre id=&quot;code_1691137019214&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;foreach (var name in query)
{
	Console.WriteLine(name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TrnsA/btsFD5zEaEe/4mjvwtg2ucOKASMWIU8NKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TrnsA/btsFD5zEaEe/4mjvwtg2ucOKASMWIU8NKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TrnsA/btsFD5zEaEe/4mjvwtg2ucOKASMWIU8NKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTrnsA%2FbtsFD5zEaEe%2F4mjvwtg2ucOKASMWIU8NKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Delegate를 제거하여 code 간소화하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Func&amp;lt;string, bool&amp;gt; delegate의 instance는 명시적으로 제거할 수 있습니다. 그러면 code를 더욱 간소화할 수 있는데 이는 C# compiler가 편의상 delegate를 instance화 할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전의 문을 복사한 뒤 그대로 주석처리하고 복사된 code를 다시 넣습니다. 그런 뒤 붙여 넣은 code에서 아래와 같이&amp;nbsp; method이름만 남겨두고 delegate의 instance를 제거합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691137433886&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//var query = names.Where(new Func&amp;lt;string, bool&amp;gt;(NameLongerThanFour));
var query = names.Where(NameLongerThanFour);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 동일한 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) Lambda 표현식 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 code를 더 간소화할 수 있는 방법으로 명명된 method를 사용하는 대신 lambda expression을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 이러한 방법이 처음에는 복잡하고 어려워 보일 수 있으나 lambda expression은 이름이 없는 함수로서 =&amp;gt; 기호(goes to라고 함)를 사용해 반환값을 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 다시 복사하여 기존의 문을 주석처리한 뒤 복사한 code를 붙여 넣어 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691208102145&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//var query = names.Where(new Func&amp;lt;string, bool&amp;gt;(NameLongerThanFour));
//var query = names.Where(NameLongerThanFour);
var query = names.Where(name =&amp;gt; name.Length &amp;gt; 3);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 보면 NameLongerThanFour method의 모든 중요한 부분이 lambda 식에 포함되어 있는 것을 알 수 있고 또 이것이 전부입니다. lambda 식은 크게 아래 2가지를 정의하고 있는데&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 매개변수의 이름 : name&lt;/li&gt;
&lt;li&gt;반환 값 표현식 : name.length &amp;gt; 3&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 name 입력 매개변수의 type은 sequence가 포함하고 있는 문자열값으로 추론되며 Where에서 가능한 delegate의 정의에 따라 반환값은 bool이 됩니다. 따라서 =&amp;gt; 기호 이후 표현식에서는 반드시 bool값을 반환하도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compiler는 우리를 위해 대부분의 작업을 수행하므로 code는 가능한 한 간소화할 수 있습니다. 예제를 실행하면 이전과 동일한 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) 매개변수에 기본값 적용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 12에서는 lambda 표현식에서 기본값을 제공할 수 있게 되었습니다. 따라서 아래 예제와 같이 표현식을 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709864753458&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//var query = names.Where(new Func&amp;lt;string, bool&amp;gt;(NameLongerThanFour));
//var query = names.Where(NameLongerThanFour);
//var query = names.Where(name =&amp;gt; name.Length &amp;gt; 3);
var query = names.Where((string name = &quot;홍&quot;) =&amp;gt; name.Length &amp;gt; 3);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) Entity 정렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 일반적으로 사용되는 method로 OrderBy와 ThenBy 등이 있으며 이들은 Sequence를 정렬하기 위해 사용됩니다. 또한 확장 method는 이전 method가 IEnumerable&amp;lt;T&amp;gt; interface를 구현하는 type인 다른 sequence를 반환하는 경우라면 연결(method chain)하여 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● OrderBy를 사용한 단일 속성으로 정렬하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제와 같이 OrderBy method를 query의 마지막에 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691209003327&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names
	.Where(name =&amp;gt; name.Length &amp;gt; 1)
	.OrderBy(name =&amp;gt; name.Length);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;각각의 확장 method마다 line을 분리하여 형식화하면 좀 더 code를 읽기 쉽게 만들 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이름이 짧은 순서대로 정렬된 결과를 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOLMuQ/btsFCZ0T4u7/QDiq34sXufSDeC9mtqcrRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOLMuQ/btsFCZ0T4u7/QDiq34sXufSDeC9mtqcrRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOLMuQ/btsFCZ0T4u7/QDiq34sXufSDeC9mtqcrRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOLMuQ%2FbtsFCZ0T4u7%2FQDiq34sXufSDeC9mtqcrRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;229&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 name이 긴 것 순으로 정렬하고자 한다면 OrderByDescending method를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● ThenBy를 사용한 다수의 속성으로 정렬하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나 이상의 속성을 통해 정렬하고자 한다면 예를 들어 alphabet이나 가나다순에서 같은 길이의 name으로 정렬하고자 한다면 아래 예제와 같이 ThenBy를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691216999339&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names
	.Where(name =&amp;gt; name.Length &amp;gt; 1)
	.OrderBy(name =&amp;gt; name.Length)
	.ThenBy(name =&amp;gt; name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 약간 다른 차이를 알 수 있을 것입니다. 같은 길이의 name으로 정렬된 상태에서 가나다순으로 정렬되었으므로 이도를 제외한 김종서와 김좌진이 상위에 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tbYLV/btsFDCdp53v/KpGMa1AUo0KxW9uhMxHdkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tbYLV/btsFDCdp53v/KpGMa1AUo0KxW9uhMxHdkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tbYLV/btsFDCdp53v/KpGMa1AUo0KxW9uhMxHdkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtbYLV%2FbtsFDCdp53v%2FKpGMa1AUo0KxW9uhMxHdkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;210&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) Item 자체에서 정렬하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서는 Order와 OrderDescending라는 확장 method가 도입되었으며 간단하게 item자체에서 졍렬을 수행할 수 있습니다. 예를 들어 string값의 sequence가 있다면 .NET 7이전에 OrderBy method를 호출하고 item자체를 선택하는 lambda식을 전달해야 했지만&lt;/p&gt;
&lt;pre id=&quot;code_1691217351046&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names.OrderBy(name =&amp;gt; name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7부터는 아래와 같이 간단하게 정렬을 수행할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691217387019&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names.Order();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderDescending 역시 역순으로 정렬한다는 점을 제외하면 Order와 동일하게 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(11) var 또는 특정 type을 사용해 query 선언하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ 표현식을 작성하는 동안 query개체를 선언하기 위해 var keyword를 사용하는 것은 LINQ 표현식이 동작하는 것마다 type이 자주 바뀔 수 있으므로 매우 일반적인 방법입니다. 예를 들어 예제에서의 query는 IEnumerable&amp;lt;string&amp;gt;으로서 시작되었으나 현재는 IOrderedEnumerable&amp;lt;string&amp;gt;이 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mouse를 var keyword에 올려두면 type이 IOrderedEnumerable&amp;lt;string&amp;gt;이라는 것을 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W7Aom/btsFABfItjL/JEctL6bjL9dJONu9adVyCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W7Aom/btsFABfItjL/JEctL6bjL9dJONu9adVyCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W7Aom/btsFABfItjL/JEctL6bjL9dJONu9adVyCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW7Aom%2FbtsFABfItjL%2FJEctL6bjL9dJONu9adVyCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;419&quot; height=&quot;86&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 var는 실제 type으로 아래와 같이 바꾸는 것이 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691217895246&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IOrderedEnumerable&amp;lt;string&amp;gt; query = names
	.Where(name =&amp;gt; name.Length &amp;gt; 1)
	.OrderBy(name =&amp;gt; name.Length)
	.ThenBy(name =&amp;gt; name);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;일단 query에 대한 작업을 끝내고 나면 var에서부터 선언된 type을 실제 type으로 바꿀 수 있고 이는 type이 정확히 어떤 것인지를 명확히 이해하는데 도움이 될 수 있습니다. 또한 compile시 var가 실제 type으로 바뀌게 되므로 성능에는 영향을 주지 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(12) Type filtering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Where 확장 method는 text나 숫자와 같이 값을 통해 filtering 할 수 있지만 sequence가 여러 type을 가지고 있고 이때 상속 계정을 따르면서 특정 type으로 filtering 해야 한다면 다른 방법을 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 예외에 대한 sequence가 있다고 가정하면 아래 그림과 같이 복잡한 계층구조로 부터 수백 가지의 예외 type이 형성될 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nzYCq/btsFDyWnBF2/KLwMunmi3RObVeMqMQGJI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nzYCq/btsFDyWnBF2/KLwMunmi3RObVeMqMQGJI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nzYCq/btsFDyWnBF2/KLwMunmi3RObVeMqMQGJI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnzYCq%2FbtsFDyWnBF2%2FKLwMunmi3RObVeMqMQGJI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1041&quot; height=&quot;388&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 실제 type에 대한 filtering을 예제를 통해 구현해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Progam.cs에서 아래와 같이 예외별 파생 개체를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691285029526&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Exception&amp;gt; exceptions = new()
{
		new ArgumentException(),
		new SystemException(),
		new IndexOutOfRangeException(),
		new InvalidOperationException(),
		new NullReferenceException(),
		new InvalidCastException(),
		new OverflowException(),
		new DivideByZeroException(),
		new ApplicationException()
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 OfType&amp;lt;T&amp;gt; 확장 method를 사용해 arithmetic exception이 아닌 예외를 삭제하고 해당 예외만 console에 표시하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691285217877&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IEnumerable&amp;lt;ArithmeticException&amp;gt; arithmeticExceptionsQuery = exceptions.OfType&amp;lt;ArithmeticException&amp;gt;();
foreach (ArithmeticException exception in arithmeticExceptionsQuery)
{
	Console.WriteLine(exception);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 ArithmeticException 혹은 ArithmeticException으로부터 파생된 type만이 표시됨을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWBJlb/btsFCfQxoXP/zl8nT1rIVAg0gDPzHmvzOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWBJlb/btsFCfQxoXP/zl8nT1rIVAg0gDPzHmvzOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWBJlb/btsFCfQxoXP/zl8nT1rIVAg0gDPzHmvzOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdWBJlb%2FbtsFCfQxoXP%2Fzl8nT1rIVAg0gDPzHmvzOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;115&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(13) LINQ를 사용한 set과 bag&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합은 수학에서 가장 기본적인 개념 중의 하나이며 set은 하나 또는 그 이상의 중복되지 않은 개체의 collection입니다. 반면 Bag라고 하는 multiset(중복집합)은 중복되는 하나 또는 그 이상의 개체에 대한 collection에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 관련하여 학교에서 우리는 이미 Venn diagram이라는 것을 배운 적이 있는데 공통집합 연산에는 집합 간의 교집합 또는 합집합을 포함합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 취미별 학생들에 대한 3개의 문자열 값의 array를 정의하고 이들에 대한 공통집합과 합집합을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 project에서 Helpers.cs이름의 class file을 partial class Program으로 추가하고 여기에 아래와 같이 method를 정의합니다. 해당 method는 문자열 변수의 모든 sequence를 comma(,)로 분리한 단일 문자열을 선택적으로 전달하는 설명과 함께 console에 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691286902614&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	static void Output(IEnumerable&amp;lt;string&amp;gt; cohort, string description = &quot;&quot;)
	{
		if (!string.IsNullOrEmpty(description))
		{
			Console.WriteLine(description);
		}

		Console.Write(&quot; &quot;);
		Console.WriteLine(string.Join(&quot;, &quot;, cohort.ToArray()));
		Console.WriteLine();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 학생이름에 대한 3개의 문자열 array를 정의하고 이들을 출력한 뒤 다양한 집합연산을 수행하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691287405006&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] reading = new[] { &quot;홍길동&quot;, &quot;박남일&quot;, &quot;김신영&quot;, &quot;조정석&quot; };
string[] music = new[] { &quot;윤태진&quot;, &quot;박길영&quot;, &quot;서만진&quot;, &quot;윤태진&quot;, &quot;김영수&quot; };
string[] exercise = new[] { &quot;최동만&quot;, &quot;이해영&quot;, &quot;이해영&quot;, &quot;윤태진&quot;, &quot;한봉남&quot; };

Output(reading, &quot;독서&quot;);
Output(music, &quot;음암감상&quot;);
Output(exercise, &quot;운동&quot;);

Output(music.Distinct(), &quot;music.Distinct()&quot;);
Output(music.DistinctBy(name =&amp;gt; name.Substring(0, 2)), &quot;music.DistinctBy(name =&amp;gt; name.Substring(0, 2)):&quot;);
Output(music.Union(exercise), &quot;music.Union(exercise)&quot;);
Output(music.Concat(exercise), &quot;music.Union(exercise)&quot;);
Output(music.Intersect(exercise), &quot;music.Intersect(exercise)&quot;);
Output(music.Except(exercise), &quot;music.Union(exercise)&quot;);
Output(reading.Zip(music, (c1, c2) =&amp;gt; $&quot;{c1} matched with {c2}&quot;), &quot;reading.Zip(music)&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5VztW/btsFB4Is5rO/RM8tskkiltKGS4ZGlAIhYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5VztW/btsFB4Is5rO/RM8tskkiltKGS4ZGlAIhYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5VztW/btsFB4Is5rO/RM8tskkiltKGS4ZGlAIhYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5VztW%2FbtsFB4Is5rO%2FRM8tskkiltKGS4ZGlAIhYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;609&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 Zip은 2개의 sequences에서 item의 수가 동일하지 않다면 일부 item에서 mathing 되지 않을 수 있습니다. 따라서 music에 '김영수'는 결과에 포함되지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DistinctBy는 전체이름을 비교하여 중복을 제거하는 대신 이름에서 처음 2글자만 비교하여 제거하기 위해 lambda key selector를 정의하였고 따라서 2명의 '윤태진'중 하나는 제거되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 우리는 LINQ to Objects 공급자를 사용하여 in-memory 개체를 대상으로 예제를 구현하였습니다. 이후부터는 LINQ to Entities 공급자를 사용하여 database에 저장된 entity를 대상으로 예제를 구현해 볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. EF Core와 LINQ&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서는 LINQ query를 통해 filter와 sort를 수행해 보았습니다. 하지만 sequence의 item 형태는 어떤 것도 변함이 없습니다. 이것은 projection이라고 하는 것으로 어떤 형태의 item을 다른 형태로 projecting 하는 것이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Projection에 관해 더 알아보려면 좀 더 복잡한 type을 가지고 해 보는 것도 좋은 방법이므로 다음 project에서는 string sequences를 사용하는 대신 이전에 설치했었던 Northwind sample database의 entity sequences를 사용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) EF Core model build&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 작업의 대상이 될 database와 table을 표현하기 위해 EF Core model을 정의해야 합니다. 따라서 우선은 Categories와 Categories table사이에 자동적으로 관계가 정의되는 것을 막고 완전한 제어권을 가져오기 위해 model을 수동적으로 정의할 것입니다. 그런 다음 LINQ를 사용하여 이 둘의 entity를 join 해 보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy11 solution에서 LinqWithEFCore이름의 console app project를 생성합니다. 그리고 LinqWithEFCore project에서 SQL Server에 대한 EF Core provider package를 설치합니다. 실제 database는 아래 글에서 이미 설치하였으므로 database를 생성하는 과정은 다루지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-10-Entity-Framework-Core&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 10. Entity Framework Core&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 project에 Northwind.cs, Category.cs, Product.cs라는 3개의 class file을 추가할 것입니다. 우선 Northwind.cs file은 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691304440261&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Northwind : DbContext
{
	public DbSet&amp;lt;Category&amp;gt; Categories { get; set; } = null!;

	public DbSet&amp;lt;Product&amp;gt; Products { get; set; } = null!;

	protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
	{
		string connection = &quot;Server=localhost;user id=sa;password=!123;Database=Northwind;MultipleActiveResultSets=true;TrustServerCertificate=true&quot;;
		optionsBuilder.UseSqlServer(connection);
	}

	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		if ((Database.ProviderName is not null))
		{
			modelBuilder.Entity&amp;lt;Product&amp;gt;().Property(product =&amp;gt; product.UnitPrice).HasConversion&amp;lt;double&amp;gt;();
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Category.cs는 아래와 같이 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1691304557572&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Category
{
	public int CategoryId { get; set; }

	[Required]
	[StringLength(15)]
	public string CategoryName { get; set; } = null!;

	public string? Description { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Product.cs는 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691304655532&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Product
{
	public int ProductId { get; set; }

	[Required]
	[StringLength(40)]
	public string ProductName { get; set; } = null!;
	public int? SupplierId { get; set; }
	public int? CategoryId { get; set; }

	[StringLength(20)]
	public string? QuantityPerUnit { get; set; }

	[Column(TypeName = &quot;money&quot;)]
	public decimal? UnitPrice { get; set; }
	public short? UnitsInStock { get; set; }
	public short? UnitsOnOrder { get; set; }
	public short? ReorderLevel { get; set; }
	public bool Discontinued { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Sequence filtering 하고 sorting 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Table로부터 sequence를 filtering 하고 sorting 하기 위해 project에 Functions.cs이름의 class를 file을 추가하고 partial Program class를 정의한 뒤 product를 filter 하고 sort 하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691305023712&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void FilterAndSort()
{
	using (Northwind db = new())
	{
		DbSet&amp;lt;Product&amp;gt; allProducts = db.Products;

		IQueryable&amp;lt;Product&amp;gt; filteredProducts = allProducts.Where(product =&amp;gt; product.UnitPrice &amp;lt; 10M);

		IOrderedQueryable&amp;lt;Product&amp;gt; sortedAndFilteredProducts = filteredProducts.OrderByDescending(product =&amp;gt; product.UnitPrice);

		Console.WriteLine(&quot;Products that cost less than $10:&quot;);

		foreach (Product p in sortedAndFilteredProducts)
		{
			Console.WriteLine(&quot;{0}: {1} costs {2:$#,##0.00}&quot;, p.ProductId, p.ProductName, p.UnitPrice);
		}

		Console.WriteLine();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 DbSet&amp;lt;T&amp;gt;는 IEnumerable&amp;lt;T&amp;gt;를 구현하고 있으므로 LINQ는 EF Core를 위해 만들어진 model의 entity를 query 하고 조작하기 위해 사용할 수 있습니다. (실제 T대신 TEntity라고 할 수 있지만 해당 generic type의 이름은 기능적인 면을 가지고 있지 않으며 단지 type이 class라는 것만 필요할 뿐입니다. 이름은 그저 class가 entity model이 될 것이라는 예상을 나타낼 뿐입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sequence는 또한 IEnumerable&amp;lt;T&amp;gt;혹은 IOrderedEnumerable&amp;lt;T&amp;gt;대신 IQueryable&amp;lt;T&amp;gt; (또는 정렬 LINQ method를 호출하고 나면 IOrderedQueryable&amp;lt;T&amp;gt;)를 구현하고 있습니다. 이는 표현식 tree를 사용해 query를 build 하는 LINQ provider를 사용한다는 것을 나타냅니다. 이들은 tree와 같은 data구조를 나타내고 있으며 동적 query의 생성을 가능하게 하는데 SQLite와 같은 외부 data 공급자를 위해 LINQ query를 build 하는데 유용하게 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ표현식은 SQL과 같은 다른 query언어로 변환될 수 있습니다. foreach나 ToArray와 같은 method를 호출하여 query를 열거하게 되면 query를 실행하게 되고 결과를 구체화하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 FilterAndSort method를 아래와 같이 호출합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsQOIg/btsFz6HcgoE/5QhT0GqQp1d6qNGU3pO1Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsQOIg/btsFz6HcgoE/5QhT0GqQp1d6qNGU3pO1Sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsQOIg/btsFz6HcgoE/5QhT0GqQp1d6qNGU3pO1Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsQOIg%2FbtsFz6HcgoE%2F5QhT0GqQp1d6qNGU3pO1Sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;324&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 해당 query가 목적한 바에 대한 결과를 표시하고 있기는 하지만 우리가 필요한 3개의 column대신 product table로부터 모든 column들을 가져오고 있기 때문에 상당히 비효휼적이라고 할 수 있습니다. 실제 생성된 SQL문을 확인해 보기 위해 FilterAndSort method를 아래와 같이 수정하고&lt;/p&gt;
&lt;pre id=&quot;code_1691306248348&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IOrderedQueryable&amp;lt;Product&amp;gt; sortedAndFilteredProducts = filteredProducts.OrderByDescending(product =&amp;gt; product.UnitPrice);

Console.WriteLine(sortedAndFilteredProducts.ToQueryString());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 다시 실행해 보면 product에 대한 filtering결과를 표시하기 전에 실행된 SQL을 다음과 같이 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;343&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CPlX1/btsFEgOCqQC/YkVw8DOixQpFWpt2KXeku1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CPlX1/btsFEgOCqQC/YkVw8DOixQpFWpt2KXeku1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CPlX1/btsFEgOCqQC/YkVw8DOixQpFWpt2KXeku1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCPlX1%2FbtsFEgOCqQC%2FYkVw8DOixQpFWpt2KXeku1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;343&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;343&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Sequence를 새로운 type으로 projecting 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 projection을 구현하기 전에 우선은 개체 초기화 구문을 다시 검토해 볼 필요가 있습니다. 정의된 class가 있다면 class명이나 new()를 사용해 해당 개체의 instance를 생성할 수 있으며 괄호를 통해 field나 속성의 초기값을 설정할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691371584371&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person
{
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
}

Person knownTypeObject = new()
{
    Name = &quot;dongjunkim&quot;,
    DateOfBirth = new(year: 1976, month: 11, day: 14)
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 3.0 이부터는 instance에서 익명 type을 허용하고 있으므로 var keyword를 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691371664242&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var knownTypeObject = new()
{
    Name = &quot;dongjunkim&quot;,
    DateOfBirth = new(year: 1976, month: 11, day: 14)
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 type을 명시하지는 않았지만 compiler는 Name과 DateOfBirth라는 이름의 2개의 속성에 대한 설정으로부터 익명 type을 추론할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 기법은 특히 새로운 type을 명시적으로 정의하지 않고 기존 type에서 새로운 type으로 project 하기 위해 LINQ query를 작성할 때 도움이 될 수 있습니다. Type은 익명이기에 var 선언된 지역변수에서만 작동할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 Product class의 instance를 새로운 익명 type으로 투영하기 위한 select method의 호출을 추가함으로써 database table을 상대로 SQL명령을 더 효휼적으로 실행하는 방식을 보여주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs의 FilterAndSort method에서 실제 필요로 하는 세 개의 속성(table의 column에 해당하는)만을 반환하는 Select method를 사용하기 위해 LINQ query를 확장하는 문을 아래와 같이 추가하고 var keyword와 projection LINQ 표현식을 사용하도록 아래와 같이 foreach문을 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691372465730&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IOrderedQueryable&amp;lt;Product&amp;gt; sortedAndFilteredProducts = filteredProducts.OrderByDescending(product =&amp;gt; product.UnitPrice);

var projectedProducts = sortedAndFilteredProducts.Select(product =&amp;gt; new { product.ProductId, product.ProductName, product.UnitPrice });

Console.WriteLine(projectedProducts.ToQueryString());

Console.WriteLine(&quot;Products that cost less than $10:&quot;);

foreach (var p in projectedProducts)
{
	Console.WriteLine(&quot;{0}: {1} costs {2:$#,##0.00}&quot;, p.ProductId, p.ProductName, p.UnitPrice);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Select method에서 new keyword와 foreach문의 var keyword에 mouse를 올려두면 이것이 익명 type임을 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ak9Xm/btsFB8D0VgK/0BFj9c7GKdMCZSt09eirv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ak9Xm/btsFB8D0VgK/0BFj9c7GKdMCZSt09eirv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ak9Xm/btsFB8D0VgK/0BFj9c7GKdMCZSt09eirv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAk9Xm%2FbtsFB8D0VgK%2F0BFj9c7GKdMCZSt09eirv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;103&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;103&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 같은 결과를 표시하지만 생성된 SQL은 더 효휼적으로 바뀌었음을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwpFn9/btsFBdZZ3oh/a2NdDA3uU9Oi3yBY7ZLQqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwpFn9/btsFBdZZ3oh/a2NdDA3uU9Oi3yBY7ZLQqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwpFn9/btsFBdZZ3oh/a2NdDA3uU9Oi3yBY7ZLQqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwpFn9%2FbtsFBdZZ3oh%2Fa2NdDA3uU9Oi3yBY7ZLQqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;311&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;311&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Sequence에 대한 join과 grouping&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Joining과 grouping을 위해 사용할 수 있는 확장 method로 아래와 같은 3가지가 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Join : 해당 method는 join 하고자 하는 sequence 및 연결시킬 left sequence와 right sequence에 해당하는 속성, 그리고 projection인 4개의 매개변수를 필요로 합니다.&lt;/li&gt;
&lt;li&gt;GroupJoin : 해당 method 역시 같은 매개변수를 필요로 하지만 일치하는 항목들을 일치하는 값에 대한 key속성과 다중 일치항목에 대한 IEnumerable&amp;lt;T&amp;gt; type을 가진 group개체로 결합합니다.&lt;/li&gt;
&lt;li&gt;ToLookup : 해당 method는 기존의 sequence에서 key를 통해 group화된 것을 토대로 새로운 data구조를 생성합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Sequence Join&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Categories와 Products라는 2개의 table을 join 하고자 하는 경우 Functions.cs에서 categories와 products를 select 하고 이들을 join 하여 출력하는 method를 아래와 같이 추가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691374125937&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void JoinCategoriesAndProducts()
{
	using (Northwind db = new())
	{
		//Products를 Categories와 join함
        var queryJoin = db.Categories.Join(
			inner: db.Products,
			outerKeySelector: category =&amp;gt; category.CategoryId,
			innerKeySelector: product =&amp;gt; product.CategoryId,
			resultSelector: (c, p) =&amp;gt; new { c.CategoryName, p.ProductName, p.ProductId }
		);
        foreach (var item in queryJoin)
        {
            Console.WriteLine(&quot;{0}: {1} is in {2}.&quot;, arg0: item.ProductId, arg1: item.ProductName, arg2: item.CategoryName);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Join에서는 outer와 inner라는 2개의 sequence가 존재합니다. 위의 예제에서 categories는 outer sequence가 되고 products는 inner sequence가 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 JoinCategoriesAndProducts method를 호출하도록 한 뒤 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wDxn8/btsFAhu4bm5/ikUFHU5BaIOdgAj2vKhE30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wDxn8/btsFAhu4bm5/ikUFHU5BaIOdgAj2vKhE30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wDxn8/btsFAhu4bm5/ikUFHU5BaIOdgAj2vKhE30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwDxn8%2FbtsFAhu4bm5%2FikUFHU5BaIOdgAj2vKhE30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;query의 끝에서 CategoryName에 대한 정렬을 위해 OrderBy method를 아래와 같이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691374427041&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var queryJoin = db.Categories.Join(
	inner: db.Products,
	outerKeySelector: category =&amp;gt; category.CategoryId,
	innerKeySelector: product =&amp;gt; product.CategoryId,
	resultSelector: (c, p) =&amp;gt; new { c.CategoryName, p.ProductName, p.ProductId }
).OrderBy(cp =&amp;gt; cp.CategoryName);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 실행해 보면 처음에는 Beverages category에 대한 모든 product들이 나오고 그다음 Condiments category에 대한 것들 등.. 의 순서로 결과가 표시됨을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAy242/btsFz8SxppW/eSNf71oMkR7fh9dZMak4cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAy242/btsFz8SxppW/eSNf71oMkR7fh9dZMak4cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAy242/btsFz8SxppW/eSNf71oMkR7fh9dZMak4cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAy242%2FbtsFz8SxppW%2FeSNf71oMkR7fh9dZMak4cK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Sequence group-Joining&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 join 하기에 사용했던 Categories와 Products 이 2개의 table에 대해 group-joining을 해봄으로써 약간의 차이를 비교해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서 group과 join을 수행하고 group name과 각 group에서의 모든 item을 표시하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691386474731&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void GroupJoinCategoriesAndProducts()
{
	using (Northwind db = new())
	{
        var queryGroup = db.Categories.AsEnumerable().GroupJoin(
			inner: db.Products,
			outerKeySelector: category =&amp;gt; category.CategoryId,
			innerKeySelector: product =&amp;gt; product.CategoryId,
			resultSelector: (c, matchingProducts) =&amp;gt; new
			{
				c.CategoryName,
				Products = matchingProducts.OrderBy(p =&amp;gt; p.ProductName)
			});

        foreach (var category in queryGroup)
        {
            Console.WriteLine(&quot;{0} has {1} products.&quot;, arg0: category.CategoryName, arg1: category.Products.Count());
            foreach (var product in category.Products)
            {
                Console.WriteLine($&quot; {product.ProductName}&quot;);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 예제에서처럼 AsEnumerable method를 호출하지 않는다면 다음과 같은 runtime 예외가 발생할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s9VGD/btsFCZT9otf/hitYZ9alKfwIdsnosIDKd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s9VGD/btsFCZT9otf/hitYZ9alKfwIdsnosIDKd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s9VGD/btsFCZT9otf/hitYZ9alKfwIdsnosIDKd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs9VGD%2FbtsFCZT9otf%2FhitYZ9alKfwIdsnosIDKd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 모든 LINQ 확장 method가 표현식 tree로부터 SQL과 같은 일부 다른 query문으로 변환되지 않기 때문입니다. 이 경우 예제는 application으로 data를 가져오기 위한 LINQ to EF Core사용을 위해 query처리를 강제하고 memory에서 더 복잡한 처리를 실행하기 위한 LINQ to Objects를 사용하는 AsEnumerable method를 호출함으로써 IQueryable&amp;lt;T&amp;gt;에서 IEnumerable&amp;lt;T&amp;gt;로 변환하고 있습니다. 하지만 대게 이러한 처리는 효율성면에서 더 떨어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 GroupJoinCategoriesAndProducts method를 호출하도록 한 뒤 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/okVQG/btsFD6yyGO0/jwyTyTGd83c0Efk8hadHB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/okVQG/btsFD6yyGO0/jwyTyTGd83c0Efk8hadHB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/okVQG/btsFD6yyGO0/jwyTyTGd83c0Efk8hadHB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FokVQG%2FbtsFD6yyGO0%2FjwyTyTGd83c0Efk8hadHB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 각 category에 포함된 product들이 query에서 정의된 것처럼 이름순으로 정렬되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● lookup을 위한 group화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ query 표현식을 통해 join과 group를 작성하는 대신 lookup 확장 method를 통해 entity를 group화한 개체를 생성하고 memory에 개체를 저장할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind의 database의 Products table을 보면 category를 위한 일부 column이 포함되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Product&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Chai&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Chang&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Aniseed Syrup&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Chef Anton&amp;rsquo;s Cajun Seasoning&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Chef Anton&amp;rsquo;s Gumbo Mix&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 category별로 product의 entity를 그룹화한 data구조를 생성하고자 한다면 아래와 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1710117865950&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ILookup&amp;lt;int, Product&amp;gt; productsByCategoryId = db.Products.ToLookup(keySelector: category =&amp;gt; category.CategoryId);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ToLookup method에서는 group화에 사용될 값으로서 key selector를 지정해야 합니다. 이를 통해 key와 값을 가진 dictionary형태의 data를 생성하게 되며 이때 key는 category ID가 되고 값은 Product 개체의 collection이 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;key&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;Chai, Chang...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;Aniseed Syrup, Chef Anton&amp;rsquo;s Cajun Seasoning, Chef Anton&amp;rsquo;s Gumbo Mix, ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;...&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Sequences 집계하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ 확장 method 중에는 집계기능을 수행하는 Average나 Sum과 같은 것들이 존재합니다. 아래 예제를 통해 이들 method를 사용하여 Products에 대한 집계정보를 확인하는 방법을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서 다음과 같이 다양한 집계 확장 method를 사용하는 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691454545650&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void AggregateProducts()
{
	using (Northwind db = new())
	{
        if (db.Products.TryGetNonEnumeratedCount(out int countDbSet))
        {
            Console.WriteLine(&quot;{0,-25} {1,10}&quot;, arg0: &quot;Product count from DbSet:&quot;, arg1: countDbSet);
        }
        else
        {
            Console.WriteLine(&quot;Products DbSet does not have a Count property.&quot;);
        }

        List&amp;lt;Product&amp;gt; products = db.Products.ToList();

        if (products.TryGetNonEnumeratedCount(out int countList))
        {
            Console.WriteLine(&quot;{0,-25} {1,10}&quot;, arg0: &quot;Product count from list:&quot;, arg1: countList);
        }
        else
        {
            Console.WriteLine(&quot;Products list does not have a Count property.&quot;);
        }

        //aggregation extension methods
        Console.WriteLine(&quot;{0,-25} {1,10}&quot;, arg0: &quot;Product count:&quot;, arg1: db.Products.Count());
        Console.WriteLine(&quot;{0,-27} {1,8}&quot;, arg0: &quot;Discontinued product count:&quot;, arg1: db.Products.Count(product =&amp;gt; product.Discontinued));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, arg0: &quot;Highest product price:&quot;, arg1: db.Products.Max(p =&amp;gt; p.UnitPrice));
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, arg0: &quot;Sum of units in stock:&quot;, arg1: db.Products.Sum(p =&amp;gt; p.UnitsInStock));
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, arg0: &quot;Sum of units on order:&quot;, arg1: db.Products.Sum(p =&amp;gt; p.UnitsOnOrder));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, arg0: &quot;Average unit price:&quot;, arg1: db.Products.Average(p =&amp;gt; p.UnitPrice));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, arg0: &quot;Value of units in stock:&quot;, arg1: db.Products.Sum(p =&amp;gt; p.UnitPrice * p.UnitsInStock));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Count를 확인하는 것은 단순한 동작처럼 보이지만 처리비용이 많이 들 수 있습니다. Products와 같은 DbSet&amp;lt;T&amp;gt;는 Count 속성을 가지고 있지 않으므로 TryGetNonEnumeratedCount는 false를 반환합니다. 하지만 products 같은 List&amp;lt;T&amp;gt;는 ICollection을 구현하므로 Count속성을 가지고 있고 따라서 TryGetNonEnumeratedCount는 true를 반환합니다. (이 경우 그 자체로 비용이 많이 드는 list를 instance화 해야 했지만 이미 list가 있고 item의 수를 알아야 한다면 이것이 더 효율적일 수 있습니다.) DbSet&amp;lt;T&amp;gt;에서 언제나 Count() 호출할 수 있지만 이는 sequence를 열거해야 하므로 느릴 수 있으며 lambda 식을 Count()에 전달하여 sequence의 count되어야할 item을 filter할 수 있지만 Count나 Length는 사용할 수 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 AggregateProducts method를 호출하도록 하고 예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byawGN/btsFCv6P1t2/PYd1hcVryjpMirH6KRO6HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byawGN/btsFCv6P1t2/PYd1hcVryjpMirH6KRO6HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byawGN/btsFCv6P1t2/PYd1hcVryjpMirH6KRO6HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyawGN%2FbtsFCv6P1t2%2FPYd1hcVryjpMirH6KRO6HK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;192&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 빈 sequence를 확인하는 방법&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 sequence를 확인하는데는 아래와 같은 방법이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Count method&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ의 Count method를 호출하여 결과가 0이상인지를 확인합니다. 이 방법은 sequence items 전체를 열거해야 하므로 좋은 방법은 아닙니다. Sequence가 ICollection 혹은 ICollection&amp;lt;T&amp;gt;를 구현하고 있으면 Count 속성이 더 효휼적인 방법일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Any method&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ의 Any method를 호출하여 true여부를 확인합니다. Count method보다는 괜찮지만 아래 2가지 방법을 사용할 수 있다면 추천하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Count 속성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sequence의 Count 속성을 사용하여 0이상인지를 확인합니다. ICollection 혹은 ICollection&amp;lt;T&amp;gt;를 구현하는 모든 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sequence는 Count속성을 가집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;● Length 속성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sequence의 Length속성을 사용하여 0이상인지를 확인합니다. 기본적으로 모든 array는 Length속성을 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Sequence가 Count나 Length속성을 가지고 있다면 해당 속성을 가장 우선적으로 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) Count에서의 주의점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amichai Mantinband은 Microsoft의 software engineer로서 C#과 .NET 개발자 stack의 흥미로운 부분에 대한 주목할만한 일을 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 그는 Twitter, LinkedIn, YouTube에 개발자들이 이 code가 무엇을 할 것이라고 생각하는지를 알아보기 위한 설문과 함께 code teaser를 post 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그의 code는 이렇습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691474166123&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IEnumerable&amp;lt;Task&amp;gt; tasks = Enumerable.Range(0, 2).Select(_ =&amp;gt; Task.Run(() =&amp;gt; Console.WriteLine(&quot;*&quot;)));
await Task.WhenAll(tasks);
Console.WriteLine($&quot;{tasks.Count()} stars!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 code의 결과는 무엇일까요?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;**2 stars!&lt;/li&gt;
&lt;li&gt;**2 stars!**&lt;/li&gt;
&lt;li&gt;****2 stars!&lt;/li&gt;
&lt;li&gt;이외 다른 것&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 설문결과 대부분 틀린 것을 골랐습니다. 여기서 말하고자 하는 핵심이 바로 이것이며 위와 같은 까다로운 질문에서 task와 함께 multi-threadiing의 세부적인 부분 까지는 아니더라도 LINQ부분을 살펴볼 수 있습니다. 위 code에서는 LINQ부분을 이해하기 위해 아래와 같이 code를 분리해 보겠습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;Enumerable.Range(0, 2)&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;0과 1의 두 정수에 대한 sequence를 반환합니다. code를 좀더 명확히 하기위해 이 부분에서 명명된 매개변수를 아래와 같이 추가할 수도 있을 것입니다.&lt;br /&gt;Enumerable.Range(start: 0, count: 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;Select(_ =&amp;gt; Task.Run(...)&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;두 숫자 각각에서 자체 thread를 가진 task를 생성합니다. 이때 _ 매개변수는 숫자값을 버리게 되며 각각의 task는 console로 *을 표시하게 됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;await Task.WhenAll(tasks);&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;두 task가 완료될때까지 main thread를 block하게 되며 2개의 *문자가 console에 표시됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;tasks.Count()&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;해당 scenario에서 LINQ Count() method가 작동하려면 sequence를 열거해야 하며 이렇게 하면 2개의 task를 다시 실행시킬 수 있습니다. 다만 이 2개의 task가 실행될 시점을 알 수 없고 2의 값이 호출한 method로 부터 반환됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;Console.WriteLine( $&quot;... stars!&quot;);&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;'2 stars!'가 console에 출력됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 우리는 **이 console에 먼저 출력되고 그다음 하나 또는 2개의 task가 *을 출력하고 그 뒤 '2 stars!'가 출력된다는 것을 알 수 있습니다. 마지막으로 하나 또는 2개의 task가 이전에 수행할 시간이 없거나 main thread가 종료되어 task가 *을 출력하기 전에 console app을 종료할 수 있는 경우 *을 출력할 수 있으므로 결과를 다음과 같을 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;**[각 task는 여기에 *을 출력할 수도 있음]2 starts![각 task는 여기에 *을 출력할 수 있음]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Amichai의 teaser에 대한 가장 정확한 답은 'Something else'일 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;반환값을 계산하기 위해 sequence전체를 열거해야 하는 Count 등의 LINQ 확장 method를 호출할 때는 주의가 필요합니다. Task와 같이 실행가능한 개체의 sequence를 사용하지 않는다 하더라도 sequence를 다시 열거하는 것은 비효율적일 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) LINQ를 사용한 paging&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skip과 Take 확장 method를 사용하면 paging을 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서 아래와 같이 array로 전달한 products의 table을 console에 출력하는 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691481143796&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputTableOfProducts(Product[] products, int currentPage, int totalPages)
{
    string line = new('-', count: 73);
    string lineHalf = new('-', count: 30);

    Console.WriteLine(line);
    Console.WriteLine(&quot;{0,4} {1,-40} {2,12} {3,-15}&quot;, &quot;ID&quot;, &quot;Product Name&quot;, &quot;Unit Price&quot;, &quot;Discontinued&quot;);
    Console.WriteLine(line);

    foreach (Product p in products)
    {
        Console.WriteLine(&quot;{0,4} {1,-40} {2,12:C} {3,-15}&quot;, p.ProductId, p.ProductName, p.UnitPrice, p.Discontinued);
    }

    Console.WriteLine(&quot;{0} Page {1} of {2} {3}&quot;, lineHalf, currentPage + 1, totalPages + 1, lineHalf);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;일반적인 programming세계에서 순서는 0부터 시작합니다. 따라서 currentPage count와 totalPages count에 대한 값을 사용자에게 표시하기 전 1을 더해줄 필요가 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 products에 대한 page를 만드는 LINQ query를 생성하고 여기서 만들어진 SQL을 출력한 뒤 products의 table을 표시하는 method에서 products의 array로 해당 결과를 전달하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691481629651&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputPageOfProducts(IQueryable&amp;lt;Product&amp;gt; products, int pageSize, int currentPage, int totalPages)
{
    // skip과 take전에 data를 정렬하여 각 page에서 data가 임의의 순서로 나오지 않도록 해야 합니다.
    var pagingQuery = products.OrderBy(p =&amp;gt; p.ProductId).Skip(currentPage * pageSize).Take(pageSize);

    OutputTableOfProducts(pagingQuery.ToArray(), currentPage, totalPages);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 사용자가 왼쪽(left) 혹은 오른쪽(right) 화살표를 눌러 database의 products를 한 번에 한 page씩 표시하도록 순환하는 method를 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691482077339&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void PagingProducts()
{
	using (Northwind db = new())
	{
		int pageSize = 10;
		int currentPage = 0;
		int productCount = db.Products.Count();
		int totalPages = productCount / pageSize;

		while (true)
		{
            OutputPageOfProducts(db.Products, pageSize, currentPage, totalPages);
            Console.Write(&quot;Press &amp;lt;- to page back, press -&amp;gt; to page forward, any key to exit.&quot;);

            ConsoleKey key = Console.ReadKey().Key;

            if (key == ConsoleKey.LeftArrow)
                if (currentPage == 0)
                    currentPage = totalPages;
                else
                    currentPage--;
            else if (key == ConsoleKey.RightArrow)
                if (currentPage == totalPages)
                    currentPage = 0;
                else
                    currentPage++;
            else
                break; // loop 탈출

            Console.WriteLine();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 PagingProducts method를 호출하도록 하고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzuW7I/btsFDG7XGg8/MIo8vl31rZcr0TdMkO2Vzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzuW7I/btsFDG7XGg8/MIo8vl31rZcr0TdMkO2Vzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzuW7I/btsFDG7XGg8/MIo8vl31rZcr0TdMkO2Vzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzuW7I%2FbtsFDG7XGg8%2FMIo8vl31rZcr0TdMkO2Vzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;480&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 예제의 결과에서 ORDER BY, LIMIT, OFFSET 등을 사용해 products의 page를 효휼적으로 가져오기 위해 사용된 SQL문의 표시는 제외하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽, 오른쪽 key를 누르면 page를 전환할 수 있고 그 외 다른 key를 누르면 program을 빠져나오게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Paging을 위해 Skip과 Take를 호출하는 경우라면 그전에 data를 정렬해야 합니다. 이는 query가 실행될 때마다 ProductId순이나 UnitPrice순 혹은 심지어 random처럼 특정 기준으로의 정렬이 요청될 수 있으므로 LINQ 공급자가 같은 순서의 data가 반환된다는 것을 보증하지 않기 때문입니다. 관계형 database에서 대게는 primary key상의 index순으로 사용하는 경우가 많습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. LINQ 구문의 향상기법&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 3.0에서는 2008년에 SQL경험을 가진 개발자가 LINQ query를 더 쉽게 만들기 위한 몇 가지 새로운 언어 keyword를 도입하였으며 이를 LINQ query 이해(comprehension) 구문이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 문자열형식의 array가 존재할 때&lt;/p&gt;
&lt;pre id=&quot;code_1691546895595&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] names = new[] { &quot;이순신&quot;, &quot;이도&quot;, &quot;신사임당&quot;, &quot;홍길동&quot;, &quot;김종서&quot;, &quot;박혁거세&quot;, &quot;김좌진&quot;, &quot;윤봉길&quot; };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름별로 filter 하고 정렬하기 위해 아래와 같이 확장 method와 lambda 식을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691547000212&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names
    .Where(name =&amp;gt; name.Length &amp;gt; 3)
    .OrderBy(name =&amp;gt; name.Length)
    .ThenBy(name =&amp;gt; name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 같은 결과를 만들어내기 위해 아래와 같이 query 이해 문을 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691547094651&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = from name in names
            where name.Length &amp;gt; 3
            orderby name.Length, name
            select name;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식이 작동할 수 있는 이유는 compiler가 query 이해 문을 이전예제와 동일하게 확장 method와 lambda 식으로 변환하기 때문입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;select keyword는 LINQ query 이해 구문에서 반드시 필요한 존재합니다. 하지만 Select 확장 method는 확장 method와 lambda 식을 사용할 때 Select를 호출하지 않으면 item전체가 암시적으로 선택되므로 선택적으로 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 확장 method가 C# keyword와 동일하게 연결되지는 않습니다. 예를 들어 Skip과 Take 확장 method는 일반적으로 많은 data에서 paging을 구현하는 데 사용되곤 합니다. Query comprehense만으로 작성될 수 없는 skip과 take query는 모두 확장 method를 사용해 다음과 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691548849066&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names
    .Where(name =&amp;gt; name.Length &amp;gt; 3)
    .Skip(80)
    .Take(10);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 괄호를 통해 query 이해 문을 감싸고 난 후 이를 확장 method로 연결하여 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691548937922&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = (from name in names
                where name.Length &amp;gt; 4
                select name)
                .Skip(80)
                .Take(10);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. 사용자 LINQ 확장 method 정의하기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글을 통해서 우리는 이미 자신만의 확장 method를 생성하는 방법을 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.02.20 - [.NET/C#] - [C# 12와 .NET 8] 6. Interface와 Class상속&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ 확장 method를 만들기 위해 해야 할 것은 IEnumerable&amp;lt;T&amp;gt; type을 확장하는 것이 전부입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;직접 정의한 method는 별도의 class file을 만들어 분리해 두면 자체 assembly나 NuGet package 등으로 배포하기 수훨해 질 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 우리는 평균을 아래 3가지 중 하나가 될 수 있음을 말할 수 있는데&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mean : 수를 합산하고 건수로 나눈 것&lt;/li&gt;
&lt;li&gt;Mode : 가장 일반적인 숫자&lt;/li&gt;
&lt;li&gt;Median : 정렬되고 난 후의 숫자 중에서 중간값&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 Average 확장 method를 mean으로 계산되도록 구현하였으므로 여기서 Average method를 위에서 언급한 Mode와 Median계산이 가능하도록 해야 한다면 다음과 같은 방법으로 method를 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project에 MyLinqExtensions.cs file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691627431010&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace LinqWithEFCore
{
    public static class MyLinqExtensions
    {
        // chain LINQ extension method
        public static IEnumerable&amp;lt;T&amp;gt; ProcessSequence&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence)
        {
            // you could do some processing here
            return sequence;
        }

        public static IQueryable&amp;lt;T&amp;gt; ProcessSequence&amp;lt;T&amp;gt;(this IQueryable&amp;lt;T&amp;gt; sequence)
        {
            // you could do some processing here
            return sequence;
        }

        // scalar LINQ extension methods
        public static int? Median(this IEnumerable&amp;lt;int?&amp;gt; sequence)
        {
            var ordered = sequence.OrderBy(item =&amp;gt; item);
            int middlePosition = ordered.Count() / 2;

            return ordered.ElementAt(middlePosition);
        }

        public static int? Median&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence, Func&amp;lt;T, int?&amp;gt; selector)
        {
            return sequence.Select(selector).Median();
        }

        public static decimal? Median(this IEnumerable&amp;lt;decimal?&amp;gt; sequence)
        {
            var ordered = sequence.OrderBy(item =&amp;gt; item);
            int middlePosition = ordered.Count() / 2;

            return ordered.ElementAt(middlePosition);
        }

        public static decimal? Median&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence, Func&amp;lt;T, decimal?&amp;gt; selector)
        {
            return sequence.Select(selector).Median();
        }

        public static int? Mode(this IEnumerable&amp;lt;int?&amp;gt; sequence)
        {
            var grouped = sequence.GroupBy(item =&amp;gt; item);
            var orderedGroups = grouped.OrderByDescending(group =&amp;gt; group.Count());

            return orderedGroups.FirstOrDefault()?.Key;
        }

        public static int? Mode&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence, Func&amp;lt;T, int?&amp;gt; selector)
        {
            return sequence.Select(selector)?.Mode();
        }

        public static decimal? Mode(this IEnumerable&amp;lt;decimal?&amp;gt; sequence)
        {
            var grouped = sequence.GroupBy(item =&amp;gt; item);
            var orderedGroups = grouped.OrderByDescending(group =&amp;gt; group.Count());

            return orderedGroups.FirstOrDefault()?.Key;
        }

        public static decimal? Mode&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence, Func&amp;lt;T, decimal?&amp;gt; selector)
        {
            return sequence.Select(selector).Mode();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class가 별도의 class library로 분리되어 있다면 LINQ 확장 method를 사용하기 위해서는 그저 class library assembly를 참조하기만 하면 됩니다. 참고로 System.Linq namespace는 이미 암시적으로 import 되는 것이 기본이기 때문에 대부분 별다른 추가작업이 필요 없지만 그렇지 않다면 System.Linq도 import 되어야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;상기 예제의 모든 method 중 하나를 제외하고 그 외 다른 method는 SQL과 같은 query언어로 변환하는 것까지 구현하지 않았기 때문에 LINQ to SQLite 또는 LINQ to SQL Server에서 사용된 것과 같은&amp;nbsp; IQueryable sequence와 함께 사용할 수 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Chainable extension method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 예제에서 처음 2개의 method는 ProcessSequence method와의 chaining이 가능한 method입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs의 FilterAndSort method에서 Products에 대한 LINQ query를 아래와 같이 변경하여 예제의 확장 method를 호출하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691630113324&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DbSet&amp;lt;Product&amp;gt; allProducts = db.Products;

IQueryable&amp;lt;Product&amp;gt; processedProducts = allProducts.ProcessSequence();
				
IQueryable&amp;lt;Product&amp;gt; filteredProducts = processedProducts.Where(product =&amp;gt; product.UnitPrice &amp;lt; 10M);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 FilterAndSort method를 호출하도록 하고 예제를 실행하면 이전과 같은 결과를 볼 수 있을 것입니다. 여기서는 예제의 ProcessSequence method가 실제 sequence를 변경하는 것은 아니지만 이러한 구현을 통해 LINQ표현식을 위 method처럼 확장할 수 있다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Mode와 median method 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 종류의 평균을 계산하기 위한 Mode and Median method를 사용하기 위해 Functions.cs에서 products의 UnitPrice와 UnitsInStock에 대한 mean, median, mode를 이전 예제에서 구현한 확장 method와 기본 내장 Average extension method를 사용해 출력하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691630717396&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void CustomExtensionMethods()
{
	using (Northwind db = new())
	{
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, &quot;Mean units in stock:&quot;, db.Products.Average(p =&amp;gt; p.UnitsInStock));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, &quot;Mean unit price:&quot;, db.Products.Average(p =&amp;gt; p.UnitPrice));
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, &quot;Median units in stock:&quot;, db.Products.Median(p =&amp;gt; p.UnitsInStock));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, &quot;Median unit price:&quot;, db.Products.Median(p =&amp;gt; p.UnitPrice));
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, &quot;Mode units in stock:&quot;, db.Products.Mode(p =&amp;gt; p.UnitsInStock));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, &quot;Mode unit price:&quot;, db.Products.Mode(p =&amp;gt; p.UnitPrice));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 CustomExtensionMethods를 호출하도록 하고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4LGD8/btsFDANrGTW/2DhcOKi26merdcFyJlHFt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4LGD8/btsFDANrGTW/2DhcOKi26merdcFyJlHFt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4LGD8/btsFDANrGTW/2DhcOKi26merdcFyJlHFt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4LGD8%2FbtsFDANrGTW%2F2DhcOKi26merdcFyJlHFt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;136&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 $18.00인 products는 4개이고 재고가 0개인 products는 5개입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. LINQ to XML&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ to XML은 query에 대한 결과를 XML로 가져올 수 있도록 하는 LINQ 공급자입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) LINQ to XML을 사용한 XML 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Products table을 XML로 변환하고자 한다면 products를 XML형식으로 출력하는 method를 다음과 같이 추가할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691653369153&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputProductsAsXml()
{
	using (Northwind db = new())
	{
        Product[] productsArray = db.Products.ToArray();

        XElement xml = new(&quot;products&quot;,
			from p in productsArray
			select new XElement(&quot;product&quot;, new XAttribute(&quot;id&quot;, p.ProductId), new XAttribute(&quot;price&quot;, p.UnitPrice), new XElement(&quot;name&quot;, p.ProductName)));
                
		Console.WriteLine(xml.ToString());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 OutputProductsAsXml method를 호출하도록 한 뒤 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HzOZj/btsFEjxQmkA/cEom8wF9ELaRAKHnZYcCmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HzOZj/btsFEjxQmkA/cEom8wF9ELaRAKHnZYcCmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HzOZj/btsFEjxQmkA/cEom8wF9ELaRAKHnZYcCmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHzOZj%2FbtsFEjxQmkA%2FcEom8wF9ELaRAKHnZYcCmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 XML의 구조는 XML 요소와 위 예제에서 선언적으로 서술된 LINQ to XML문의 attribute와 일치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) LINQ to XML을 사용한 XML parsing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ to XML을 사용하면 XML file을 쉽게 질의하거나 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project에 settings.xml이름의 XML file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691654877066&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;
&amp;lt;memberSettings&amp;gt;
	&amp;lt;add key=&quot;Name&quot; value=&quot;hong&quot; /&amp;gt;
	&amp;lt;add key=&quot;Age&quot; value=&quot;30&quot; /&amp;gt;
	&amp;lt;add key=&quot;WorkGroup&quot; value=&quot;IT&quot; /&amp;gt;
&amp;lt;/memberSettings&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio 2022는 compile 된 application이 bin\Debug\net7.0 folder에서 실행되므로 추가한 XML을 해당 folder로 복사되도록 해야 합니다. 따라서 XML file을 선택하고 'Copy to Output Directory'의 속성을 'Copy always'로 변경해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서는 아래와 같은 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691655196289&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void ProcessSettings()
{
	string path = Path.Combine(Environment.CurrentDirectory, &quot;settings.xml&quot;);

	Console.WriteLine($&quot;Settings file path: {path}&quot;);

	XDocument doc = XDocument.Load(path);
	var memberSettings = doc.Descendants(&quot;memberSettings&quot;).Descendants(&quot;add&quot;)
	.Select(node =&amp;gt; new
	{
		Key = node.Attribute(&quot;key&quot;)?.Value,
		Value = node.Attribute(&quot;value&quot;)?.Value
	}).ToArray();
	foreach (var item in memberSettings)
	{
		Console.WriteLine($&quot;{item.Key}: {item.Value}&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 method는 XML file을 load 하여 LINQ to XML을 사용해 memberSettings이름의 요소와 하위 add요소를 찾습니다. 그런 다음 XML을 Key와 Value 속성을 가진 익명 type의 array로 구체화한뒤 이를 열거하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 ProcessSettings method를 호출하고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdAw85/btsFAFvGkGW/Rp0fMcfUnRqRzZMzQbiMkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdAw85/btsFAFvGkGW/Rp0fMcfUnRqRzZMzQbiMkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdAw85/btsFAFvGkGW/Rp0fMcfUnRqRzZMzQbiMkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdAw85%2FbtsFAFvGkGW%2FRp0fMcfUnRqRzZMzQbiMkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;109&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>c#</category>
      <category>LINQ</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/601</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-11-LINQ#entry601comment</comments>
      <pubDate>Mon, 11 Mar 2024 12:04:55 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 10. Entity Framework Core</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-10-Entity-Framework-Core</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Entity Framework Core (이하 EF Core)는 SQLite나 SQL Server와 같은 관계형 database에 data를 읽고 쓰기 위한 객체-데이터 저장 mapping 기술입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. Database&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database에는 크게 2가지 종류가 있는데 하나는 RDBMS(Relational Database Management System)으로 SQL Server, PostgreSQL, MySQL, SQLite 등이 있고 다른 하나는 NoSQL로서 Azure Cosmos DB, Redis, MongoDB, Apache Cassandra 등이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 database는 1970년대 개발된 것으로 SQL(Structured Query Language)을 통해 data를 질의합니다. 그 당시 data를 저장하는데는 많은 비용이 소모되었으므로 가능한 한 data의 중복을 최소화해야 했습니다. Data는 행(Row)과 열(Column)로 이루어진 table구조에 저장되었는데 일단 완성되고 나면 refactor 하기에는 까다로운 구조였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 단순히 SQL이 없음을 의미하지는 않습니다. 다른 뜻으로는 굳이 SQL일 필요가 없다는 의미가 될 수도 있습니다. 2000년대부터 개발되어온 NoSQL은 당시 Internet과 Web이 대중화되고 software에 관한 많은 학습이 이루어지던 시기였으며 또한 막대한 확장성과 고성능을 위해 설계되었고 최대한의 유연성을 제공함으로써 개발자들의 집입장벽을 낮추었습니다. 또한 구조를 강제하지 않음으로써 이들 사이에서는 언제든 schema를 변경할 수 있는 특징이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 과거 Entity Framework&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity Framework는 2008년 말 Service pack 1과 함께 .NET Framework 3.5의 일부로 처음 출시되었습니다. 그런 후 Entity Framework는 지금까지 계속해서 발전을 거듭하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORM은 mapping정의를 사용하여 class의 속성을 table의 column으로 연결합니다. 그러면 개발자는 관계형 table 혹은 NoSQL data store에 의해 제공된 다른 구조로 값을 어떻게 저장해야 할지 알아야 하는 대신 자신에게 친숙한 방식으로 다른 type 간 개체를 통해 상호작용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Framework에 포함된 EF의 version은 Entity Framework 6 (EF6)로서 성숙되고 안정적이며 복잡한 상속 model뿐 아니라 model을 정의하는 EDMX(XML file)와 그 외 몇 가지 향상된 기능을 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF 6.3부터는 .NET Framework에서 별도의 package로 분리되어 .NET Core 3.0부터 지원이 가능하게 되었습니다. Web application이나 service와 같은 기존 project에서 cross-platform으로 이식하고 동작할 수 있으나 EF6는 cross-platform에서 동작시 일부 제한적이며 더 이상 새로운 기능이 추가되지 않으므로 legacy technology로 간주되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Entity Framework의 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Legacy technology인 Entity Framework를 .NET Core 3.0이나 이후 project에서 사용하려면 project file에서 아래와 같이 package 참조를 추가해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689562026849&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PackageReference Include=&quot;EntityFramework&quot; Version=&quot;6.4.4&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예전 WPF app을 migration하는 경우와 같은 상황만 필요하다면 EF6를 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Entity Framework Core&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진정한 cross-platform version인 EF Core는 Entity Framework와의 가장 큰 차이라고 할 수 있습니다. 비록 EF Core가 이름이 비슷하기는 하지만 EF6와는 분명히 다른 것입니다. 가장 최신의 EF Core version은 8이며 .NET8과 일치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 5부터는 .NET5와 그 이후만 지원하며 그 이하 EF Core 3은 .NET Standard 2.1을 지원하는 platform에서만 동작합니다. EF Core 3부터는 .NET Framework 4.8과 같은 .NET Standard 2.0 platform을 지원하지 않습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;EF Core 8은 .NET 8부터의 version을 target으로 합니다. 다시 말해 EF Core 8에서의 모든 새로운 기능은 .NET 8혹은 .NET 9에서만 사용할 수 있습니다. 만약 현재 .NET 7을 사용 중이라면 version 8에서 참조하는 EF Core package를 upgrade 할 것을 권장합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 RDBMS뿐만 아니라 EF Core는 cloud기반의 비관계형 Database인 Azure Cosmos DB와 MongoDB등도 지원하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core는 많은 개선점을 가지고 있지만 이 모두를 다루지는 않을 것입니다. 예를 들어 EF Core 7과 함께 도입된 새로운 기능에는 JSON 문서를 저장하는 column을 가질 수 있는 database가 해당 문서를 질의하고 filter와 정렬 표현식에서 문서의 요소를 사용하는 JSON column을 지원하지만 EF Core 7에서 JSON column 기능은 단지 SQL Server를 위해서만 구현됩니다. 향후 EF Core version은 SQLite와 같은 다른 database도 추가될 테고 그 시점에 해당 내용을 다루게 될지도 모르지만 현재는 개발자가 알아야 할 기본적인 사항과 일부 유용한 기능을 살펴보는데만 집중할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Database First와 Code First&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF core를 사용하기 위해서는 아래 2가지 방법을 선택할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database First : Database가 이미 존재하는 상태에서 구조및 특징과 일치하는 model을 build 합니다.&lt;/li&gt;
&lt;li&gt;Code First : Database가 존재하지 않는다면 model을 build 한 뒤 해당 구조 및 특징과 일치하는 database를 EF Core를 사용해 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) EF Core에서의 성능 향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core team은 EF Core의 성능향상을 위해 많은 노력을 기울여 왔습니다. 예를 들어 EF Core 7이 SaveChanges가 호출될 때 database에 대해 단일문만이 실행된다고 식별하는 경우 이전 version과 마찬가지로 명시적인 transaction을 생성하지 않습니다. 이와 같은 방식은 일반적인 경우에서 약 25% 정도의 성능향상을 기대할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 내부적으로 적용된 최신의 개선사항들은 이들이 어떻게 동작하는가에 대한 이해가 없이도 사용하는 것만으로 그에 대한 혜택을 얻을 수 있습니다. 만약 이러한 세부적인 사항에 관심이 있다면(특히 일부 멋진 SQL Server 기능을 활용한 방식에 대해) EF Core team이 작성한 아래 글을 읽어보시길 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot;&gt;Announcing Entity Framework Core 7 Preview 6: Performance Edition (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689580629937&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Announcing Entity Framework Core 7 Preview 6: Performance Edition - .NET Blog&quot; data-og-description=&quot;Announcing Entity Framework Core 7 Preview 6 with update performance optimizations.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Announcing Entity Framework Core 7 Preview 6: Performance Edition - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Announcing Entity Framework Core 7 Preview 6 with update performance optimizations.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot;&gt;Announcing Entity Framework Core 6.0 Preview 4: Performance Edition (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689580636669&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Announcing Entity Framework Core 6.0 Preview 4: Performance Edition - .NET Blog&quot; data-og-description=&quot;EF Core 6.0 Preview 4 is here and has the best performance ever. Read this post to learn how the team boosted speed by 70% over EF Core 5.0!&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sCgvV/hyTleD3YqP/PzJ3b4mEMJF1TkyKcAedpk/img.png?width=1605&amp;amp;height=552&amp;amp;face=0_0_1605_552,https://scrap.kakaocdn.net/dn/bbwevS/hyTmoSAjeD/E9PwrgPZgCk3zqeJsntUN0/img.jpg?width=460&amp;amp;height=460&amp;amp;face=109_131_349_393,https://scrap.kakaocdn.net/dn/F2Af2/hyTk60iSUo/cisreKRM244C1xFjPphq5K/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sCgvV/hyTleD3YqP/PzJ3b4mEMJF1TkyKcAedpk/img.png?width=1605&amp;amp;height=552&amp;amp;face=0_0_1605_552,https://scrap.kakaocdn.net/dn/bbwevS/hyTmoSAjeD/E9PwrgPZgCk3zqeJsntUN0/img.jpg?width=460&amp;amp;height=460&amp;amp;face=109_131_349_393,https://scrap.kakaocdn.net/dn/F2Af2/hyTk60iSUo/cisreKRM244C1xFjPphq5K/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Announcing Entity Framework Core 6.0 Preview 4: Performance Edition - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;EF Core 6.0 Preview 4 is here and has the best performance ever. Read this post to learn how the team boosted speed by 70% over EF Core 5.0!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) EF Core를 사용한 console app 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 console app을 만들기 위해 csStudy10 solution을 생성하고 그 안에 WorkingWithEFCore이름의 Console App project를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Sample database&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET을 통해 RDBMS를 어떻게 사용할 수 있을지를 알아보기 위해서는 적당한 수의 record와 적당한 수준의 복잡도를 가진 sample database가 필요합니다. 이를 위해 Microsoft는 몇 가지 sample database를 제공하고 있는데 너무 복잡한 것을 제외하고 사용가능한 가장 적합한 database로 1990년대 가장 먼저 생성된 database인 Northwind를 사용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격지나 로컬에 MSSQL Server를 설치하고 Northwind Database를 아래 script file로 생성합니다. MSSQL에 관한 제사한 사항은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Server/SQL Server] - [MSSQL] MS SQL Server 다운로드 및 설치/설정&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689648100728&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[MSSQL] MS SQL Server 다운로드및 설치/설정&quot; data-og-description=&quot;해당 포스팅에서 설명하는 MS SQL Server는 2019 Developer Edtion입니다. 인터넷에서 아래 주소로 이동합니다. SQL Server 다운로드 | Microsoft SQL Server 다운로드 | Microsoft 지금 Microsoft SQL Server 다운로드를 시&quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot; data-og-url=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/doAEh3/hyTk9pBH0B/25F1Xb51nkn3Ey3g9t99GK/img.png?width=800&amp;amp;height=490&amp;amp;face=0_0_800_490,https://scrap.kakaocdn.net/dn/9Yjv7/hyTk6NdivL/kKQZcjbDZJZHUKgxRfKQwk/img.png?width=800&amp;amp;height=490&amp;amp;face=0_0_800_490,https://scrap.kakaocdn.net/dn/q83Yn/hyTmyg06Gr/ekUfGE7W82TRVWwBs6nM6K/img.png?width=2048&amp;amp;height=1256&amp;amp;face=0_0_2048_1256&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/doAEh3/hyTk9pBH0B/25F1Xb51nkn3Ey3g9t99GK/img.png?width=800&amp;amp;height=490&amp;amp;face=0_0_800_490,https://scrap.kakaocdn.net/dn/9Yjv7/hyTk6NdivL/kKQZcjbDZJZHUKgxRfKQwk/img.png?width=800&amp;amp;height=490&amp;amp;face=0_0_800_490,https://scrap.kakaocdn.net/dn/q83Yn/hyTmyg06Gr/ekUfGE7W82TRVWwBs6nM6K/img.png?width=2048&amp;amp;height=1256&amp;amp;face=0_0_2048_1256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[MSSQL] MS SQL Server 다운로드및 설치/설정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 포스팅에서 설명하는 MS SQL Server는 2019 Developer Edtion입니다. 인터넷에서 아래 주소로 이동합니다. SQL Server 다운로드 | Microsoft SQL Server 다운로드 | Microsoft 지금 Microsoft SQL Server 다운로드를 시&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 해당 Database의 전체 구조는 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;993&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0vuNG/btsFipf2hko/ZoFBWVkHH6KBDn7sbZ1ls0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0vuNG/btsFipf2hko/ZoFBWVkHH6KBDn7sbZ1ls0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0vuNG/btsFipf2hko/ZoFBWVkHH6KBDn7sbZ1ls0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0vuNG%2FbtsFipf2hko%2FZoFBWVkHH6KBDn7sbZ1ls0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;976&quot; height=&quot;993&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;993&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조를 보면 다음과 같은 사항을 알 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 category는 고유한 식별자, 이름, 설명, 이미지를 갖고 있습니다.&lt;/li&gt;
&lt;li&gt;각 product는 고유한 식별자, 이름, 단위 단가, 재고 단위 및 기타 field를 갖고 있습니다.&lt;/li&gt;
&lt;li&gt;각 product는 category의 고유한 식별자를 통해 특정 category에 할당됩니다.&lt;/li&gt;
&lt;li&gt;Category와 product의 관계는 각 category가 product를 0개 이상 가질 수 있는 1대다의 관계입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/P6Y0O/btsFkVk7RYC/ok44lV6uOEzTchU0gRDYSk/northwnd.sql?attach=1&amp;amp;knm=tfile.sql&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;northwnd.sql&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;1.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. EF Core 설정&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core를 사용하기 전 간단히 어떤 EF Core database 공급자를 선택할 수 있는지를 알아보도록 하겠습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) ADO.NET database provider&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity Framework이전에는 ADO.NET이 사용되었습니다. ADO.NET은 EF와 비교해 database를 대상으로한 더 단순하면서도 더 효휼적인 API인데 DbConnection, DbCommand, and DbReader같은 추상화 class와 SqlConnection과&amp;nbsp; SqlCommand같은 공급자별 구현을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 application에 대한 database로 MS SQL Server를 사용하기로 했다면 SqlConnectionStringBuilder class를 통해 정확한 연결문자열을 사용해야 합니다. 해당 class에는 database 연결 문자열에 대한 모든 가능한 속성을 가지고 있고 이를 개별적으로 설정하여 완성된 연결문자열을 얻을 수 있습니다. 또한 비밀번호와 같은 민감한 정보의 경우에는 source code에 직접 작성하는 대신 환경변수나 별도의 관리 system에서 가져올 수 있도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 SQLite라면 연결문자열은 굳이 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SqlConnectionStringBuilder class를 사용하지 않아도 될만큼 간단하므로 굳이 class를 사용할 필요는 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite와 SQL Server에 대한 EF Core database 공급자는 항상 ADO.NET Library위에 구현되므로 본질적으로 ADO.NET보다 더 느립니다. 게다가 ADO.NET은 더 나은 성능을 위해 독립적으로 사용될 수 있지만 EF Core database 공급자는 ORM의 특징에 더 중심을 두기 때문에 성능면에서 ADO.NET이 유리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 EF Core은 아직 AOT 배포를 지원하지 않으므로 만약 AOT를 사용하기로 했다면 EF Core대신 ADO.NET Library를 사용해야 합니다. 물론 EF Core team은 EF Core를 native AOT에 어떻게 지원할지를 살펴보고 있지만 시간적으로 꽤 걸릴것으로 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ADO.NET에 관한 더 자세한 내용은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/sql/connect/ado-net/microsoft-ado-net-sql-server?view=sql-server-ver16&quot;&gt;Microsoft ADO.NET - ADO.NET Provider for SQL Server | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) EF Core database provider (공급자)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 database의 data에 접근하기 위해서는 어떻게 database와 효휼적으로 data를 주고받을지를 알고 있는 class를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core database 공급자는 이러한 일련의 class들을 말하며 특정한 database에 최적화되어 있습니다. 이러한 공급자들 중에는 심지어 현재 process의 memory에 data를 저장하는 공급자도 존재하며 특히 외부 system과의 충돌을 피할 수 있기 때문에 고성능 단위 test에서 사용하기 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래표는 저장소별 NuGet package에 배포된 package를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;용도&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;NuGet Package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;SQL Server 2012 부터&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Microsoft.EntityFrameworkCore.SqlServer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;SQLite 3.7 부터&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Microsoft.EntityFrameworkCore.SQLite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;In-memory&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Microsoft.EntityFrameworkCore.InMemory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;Azure Cosmos DB SQL API&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Microsoft.EntityFrameworkCore.Cosmos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;MySQL&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;MySQL.EntityFrameworkCore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;Oracle DB 11.2&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Oracle.EntityFrameworkCore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;PostgreSQL&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Npgsql.EntityFrameworkCore.PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 하나의 project에서 다수의 EF Core database 공급자를 설치할 수 있으며 각 package는 공급자별 type뿐만 아니라 필요한 여러 공유 type을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Database 연결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database연결을 위해서는 연결을 위한 연결문자열을 설정해야 합니다. 위에서 MSSQL Server를 설치했다는 가정하에 해당 server IP 및 login 계정 등의 정보를 묶어 하나의 연결문자열을 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Northwind database context class 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind class는 database를 표현하는 데 사용될 것입니다. 여기에 EF core를 사용하기 위해서 class는 DbContext로부터 상속받아야 합니다. 해당 class는 database와 어떻게 소통할지를 알고 있으며 data의 질의와 수정/삭제등의 조작을 위해 동적으로 SQL문을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext로 부터 파생된 class는 OnConfiguring라는 이름의 override 된 method를 갖게 되는데 해당 method를 통해 database 연결 문자열을 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 project에서 MSSQL을 위한 EF Core 공급자인 Microsoft.EntityFrameworkCore.SqlServer NuGet package를 설치하고 Northwind.cs 이름의 class file을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind.cs file에서는 EF Core에 대한 namespace를 import한뒤 Northwind class를 정의하고 DbContext부터 상속받도록 합니다. 그리고 OnConfiguring method에서 MSSQL Server를 사용하기 위한 option builder를 아래와 같이 구성해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689662682446&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Northwind : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string connection = &quot;Server=localhost;user id=sa;password=!123;Database=Northwind;MultipleActiveResultSets=true&quot;;
        Console.WriteLine($&quot;Connection: {connection}&quot;);
        optionsBuilder.UseSqlServer(connection);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 사용하는 database 공급자를 확인할 수 있는 구문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689662808467&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Northwind db = new();
Console.WriteLine($&quot;Provider: {db.Database.ProviderName}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다. 이를 통해 database 연결문자열과 사용 중인 database 공급자를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L8LbA/btsFiq0lOwW/Wsk5DSlu2eslkHlKmDanmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L8LbA/btsFiq0lOwW/Wsk5DSlu2eslkHlKmDanmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L8LbA/btsFiq0lOwW/Wsk5DSlu2eslkHlKmDanmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL8LbA%2FbtsFiq0lOwW%2FWsk5DSlu2eslkHlKmDanmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;85&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. EF Core model 정의&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core는 규칙과 annotation attribute의 결합 및 Fluent API 문을 사용하여 runtime에서 entity model을 build 합니다. 따라서 class상에서 수행되는 모든 동작은 나중에 자동적으로 실제 database에서 수행되는 동작으로 변환될 수 있습니다. entity class는 table의 구조를 나타내며 class의 instance는 table의 row로서 표현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 예제를 통해 model을 정의하기 위한 3가지 방식을 검토하고 이러한 기술을 구현하는 몇 가지 class를 만들어볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) model을 정의하기 위한 EF Core 규칙&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제로 작성될 code는 아래 규칙을 사용할 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Table의 이름은 예를 들어 Products와 같이 DbContext class안에 있는 DbSet&amp;lt;T&amp;gt; 속성의 이름과 연결된다고 간주합니다.&lt;/li&gt;
&lt;li&gt;Column의 이름은 예를 들어 ProductId와 같이 entity model class에 있는 속성의 이름과 연결된다고 간주합니다.&lt;/li&gt;
&lt;li&gt;string .NET type은 database의 nvarchar type이 된다고 간주합니다.&lt;/li&gt;
&lt;li&gt;int .NET type은 database의 int type이 된다고 간주합니다.&lt;/li&gt;
&lt;li&gt;Primary key는 Id 혹은 ID이름의 속성이 된다고 간주합니다. 그런데 속성의 이름은 entity model class가 Product로 명명될 때 ProductId 혹은 ProductID로 명명될 수 있습니다. 해당 속성이 integer type 이거나 Guid type이라면 이것은 또한 IDENTITY column(insert시 자동적으로 값이 부여됨)이 될 수 있다고 간주합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에 알아야 할 많은 규칙들이 존해하며 자신만의 것을 정의할 수도 있습니다. 이와 관련해서는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot;&gt;Creating and Configuring a Model - EF Core | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689664291146&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Creating and Configuring a Model - EF Core&quot; data-og-description=&quot;Overview of creating and configuring a Entity Framework Core model via Fluent API, Data Annotations and conventions.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dz7bmr/hyTlc05tWy/LspbpkUNrgEzMlBqh5b37K/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/kg0a0/hyTmrCkJsL/51muDfXMvLCvpt4fCEVQ0K/img.png?width=1324&amp;amp;height=814&amp;amp;face=0_0_1324_814&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dz7bmr/hyTlc05tWy/LspbpkUNrgEzMlBqh5b37K/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/kg0a0/hyTmrCkJsL/51muDfXMvLCvpt4fCEVQ0K/img.png?width=1324&amp;amp;height=814&amp;amp;face=0_0_1324_814');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Creating and Configuring a Model - EF Core&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Overview of creating and configuring a Entity Framework Core model via Fluent API, Data Annotations and conventions.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Model을 정의하기 위한 EF Core annotation attribute 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 위에서 언급한 규칙만으로는 database개체와 class를 완전히 연결시키에 충분하지 못할 수 있습니다. 이때 model을 더욱 database에 근접시키기 위한 가장 간단한 방법은 annotation attribute를 적용하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 일반적으로 사용되는 몇 가지 attribute를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Attribute&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[Required]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;값은 null이 될 수 없습니다. .NET 8에서는 DisallowAllDefaultValues 매개변수를 통해 예를 들어 int가 0이 되는 것처럼 type이 그에 따른 기본값이 되는것을 방지할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[StringLength(50)]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;값은 50문자길이를 넘을 수 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[Range(1, 10)]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;값이 지정된 범위에 있도록 합니다. 이와 관련하여 .NET 8에서는 MinimumIsExclusive와 MaximumIsExclusive라는 매개변수가 존재합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[RegularExpression(표현식)]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;값은 지정한 정규표현식과 일치해야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[Column(TypeName = &quot;money&quot;, Name = &quot;UnitPrice&quot;)]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;Column의 이름(Name)과 Type(TypeName)을 지정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 database에서 product name의 최대 길이는 40이며 값은 null일 수 없습니다. 이와 같은 사실은 어떻게 Products라는 table이 지정한 data type, key와 기타 제약사항을 가진 column과 함께 생성될지를 정의하는 DDL(Data Definition Language) 문을 통해 알 수 있는데&lt;/p&gt;
&lt;pre id=&quot;code_1689731574142&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE [dbo].[Products](
	[ProductId] [int] IDENTITY(1,1) NOT NULL,
	[ProductName] [nvarchar](40) NOT NULL,
	[SupplierId] [int] NULL,
	[CategoryId] [int] NULL,
	[QuantityPerUnit] [nvarchar](20) NULL,
	[UnitPrice] [money] NULL,
	[UnitsInStock] [smallint] NULL,
	[UnitsOnOrder] [smallint] NULL,
	[ReorderLevel] [smallint] NULL,
	[Discontinued] [bit] NOT NULL,
 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED 
(
	[ProductId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 사항을 Product class에 반영하려면 아래와 같이 attribute를 적용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689731680506&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Required]
[StringLength(40)]
public string ProductName { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 경우에는 .NET type과 database type사이를 연결시키는 데 사용가능한 attribute가 분명하지 않을 수 있습니다. 예를 들어 database에서 Products의 UnitPrice column type이 money인데 .NET은 money라는 type이 존재하지 않으므로 이런 경우에는 아래와 같이 data범위가 유사한 decimal을 대신 사용하고 Column attribute를 통해 database의 type을 명시합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689732042362&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Column(TypeName = &quot;money&quot;)]
public decimal? UnitPrice { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Model을 정의하기 위한 EF Core Fluent API 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model을 정의할 수 있는 또 다른 방법은 Fluent API를 사용하는 것입니다. API는 attribute대신 사용할 수 있으며 뿐만 아니라 속성 외에도 사용할 수 있습니다. 예를 들어 ProductName속성을 정의하기 위해 속성에 2개의 attribute를 적용하는 대신 동등한 Fluent API문을 database context class의 OnModelCreating method에 아래와 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689732450857&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;modelBuilder.Entity&amp;lt;Product&amp;gt;()
 .Property(product =&amp;gt; product.ProductName)
 .IsRequired()
 .HasMaxLength(40);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 entity model class를 단순하게 유지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Fluent API를 사용한 data 제공&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fluent API를 사용함으로써 생기는 또 다른 이점은 database를 채우기 위해 초기 data를 제공할 수 있다는 것입니다. EF Core는 반드시 실행되어야 하는 insert, update, delete와 같은 동작을 자동으로 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 새로운 database에서 Product table은 최소 하나의 행을 가져야 한다면 HasData method를 아래와 같이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689732856913&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;modelBuilder.Entity&amp;lt;Product&amp;gt;().HasData(new Product {
    ProductId = 1,
    ProductName = &quot;Chai&quot;,
    UnitPrice = 8.99M
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 사용할 model은 이미 data가 존재하는 database와 연결하게 될 것이므로 위와 같은 방법은 사용하지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Northwind table에 대한 EF Core model 구축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 EF Core model을 정의하는 방법을 알게 되었으므로 이를 통해 Northwind database에 대한 2개 table을 표현하는 model을 만들 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개 entity class는 서로를 참조할 것이므로 compiler error를 피하기 위해 처음에는 어떠한 member도 없이 class를 만들 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithEFCore project에서 Category.cs와 Product.cs이름의 class file을 추가합니다. 그리고 Category.cs에서 Category class를 정의하고 Product.cs에서 Product class를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689751762608&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace WorkingWithEFCore
{
    public class Category
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1689751775224&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace WorkingWithEFCore
{
    public class Product
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Category와 Product entity class정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity model인 Category class는 Categories table의 row를 나타내는 데 사용될 것입니다. 이 table은 아래와 같이 4개의 column을 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689751960880&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE [dbo].[Categories](
	[CategoryId] [int] IDENTITY(1,1) NOT NULL,
	[CategoryName] [nvarchar](15) NOT NULL,
	[Description] [ntext] NULL,
	[Picture] [image] NULL,
 CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED 
(
	[CategoryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 model을 정의하기 위한 다음의 규칙을 감안해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3~4개의 속성 (Picture column은 사용하지 않을 것입니다.)&lt;/li&gt;
&lt;li&gt;Primary key&lt;/li&gt;
&lt;li&gt;Products table과의 1:N 관계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Description column을 database type과 정확히 연결시키기 위해서는 string 속성에 Column attribute에 대한 적용이 필요합니다. 나중에는 Fluent API를 사용하여 CategoryName이 null일 수 없고 최대 15자로 제한됨을 정의할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Category entity model class를 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689752586814&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.ComponentModel.DataAnnotations.Schema; // [Column] attribute

namespace WorkingWithEFCore
{
    public class Category
    {
        public Category()
        {
            // products를 Cateogry로 추가시킬 수 있도록 하기 위해서는 navigation 속성을 빈 collection으로 초기화 해야 합니다.
            Products = new HashSet&amp;lt;Product&amp;gt;();
        }

        //아래 속성은 database의 column과 일치합니다.
        public int CategoryId { get; set; }

        public string? CategoryName { get; set; }

        [Column(TypeName = &quot;ntext&quot;)]
        public string? Description { get; set; }

        //Table간의 관계를 나타내기 위해 정의하는 navigation속성입니다.
        public virtual ICollection&amp;lt;Product&amp;gt; Products { get; set; }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Product class도 역시 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689752905654&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.ComponentModel.DataAnnotations; // [Required], [StringLength]
using System.ComponentModel.DataAnnotations.Schema; // [Column]

namespace WorkingWithEFCore
{
    public class Product
    {
        public int ProductId { get; set; } // primary key

        [Required]
        [StringLength(40)]
        public string ProductName { get; set; } = null!;

        [Column(&quot;UnitPrice&quot;, TypeName = &quot;money&quot;)]
        public decimal? Cost { get; set; } // Column attribute를 적용함으로서 여기서 속성의 이름은 column의 이름으로 사용되지 않습니다.

        [Column(&quot;UnitsInStock&quot;)]
        public short? Stock { get; set; }

        public bool Discontinued { get; set; }

        // 이 2개의 속성은 Categories table에 대한 외래key를 정의합니다.
        public int CategoryId { get; set; }
        public virtual Category Category { get; set; } = null!;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 다음 사항에 주목합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Product class는 Products table의 row를 표현하는 데 사용되며 10개 정도의 column을 가지고 있습니다.&lt;/li&gt;
&lt;li&gt;Table로부터의 모든 column을 class의 속성으로 포함시킬 필요는 없습니다. 예제에서도 단지 ProductId, ProductName, UnitPrice, UnitsInStock, Discontinued, CategoryId에 대한 6개의 속성만을 일치시키고 있습니다.&lt;/li&gt;
&lt;li&gt;속성으로 일치되지 않는 column은 class를 통해 읽거나 설정할 수 없습니다. 새로운 개체를 생성하는데 class를 사용한다면 table의 새로운 row에서 일치되지 않는 column은 NULL이 되거나 다른 기본값이 됩니다. 누락된 column이 생기는 경우 이들 column은 선택적이거나 database에서 기본값을 설정했거나 runtime에서 예외가 발생하는지를 확인해야 합니다. 지금 예제의 경우에 row는 이미 data값을 가지고 있으며 application에서 해당 값들을 읽을 필요가 없으므로 무시하기로 합니다.&lt;/li&gt;
&lt;li&gt;예제에서 Cost속성처럼 Column의 이름과는 다른 이름으로 속성을 정의할 수도 있습니다. 이런 경우 속성에 [Column] attribute를 적용하고 여기에 UnitPrice처럼 column의 이름을 지정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;마지막 속성인 CategoryId는 각 product에서 부모 category와 일치시키는 데 사용되는 Category 속성과 연결됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 entity를 연결시키는 2개의 속성인 Category.Products와 Product.Category는 둘 다 virtual로 수식되었습니다. 이는 EF Core가 lazy loading과 같이 추가 기능을 제공하기 위해 해당 속성을 상속하고 override 할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Northwind database context class에 table추가하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext로부터 파생된 class에서는 최소 하나의 DbSet&amp;lt;T&amp;gt; type에 대한 속성을 정의해야 합니다. 이 속성은 table을 의미합니다. EF Core에게 table의 가진 각 column이 어떤 것인지를 말해주기 위해 DbSet&amp;lt;T&amp;gt;속성은 generic을 사용하여 table의 row를 나타내는 class를 지정하고 해당 class는 column을 나타내는 속성을 가지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext 파생 class는 선택적으로 OnModelCreating이름의 override method를 가질 수 있습니다. 여기에 Fluent API를 class에 attribute를 적용하는 대신 작성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind class에서 Categories와 Products에 대한 2개의 table과 OnModelCreating method를 정의하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689823022563&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Northwind : DbContext
{
    public DbSet&amp;lt;Category&amp;gt;? Categories { get; set; }
    public DbSet&amp;lt;Product&amp;gt;? Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string connection = &quot;Server=localhost;user id=sa;password=!123;Database=Northwind;MultipleActiveResultSets=true&quot;;
        Console.WriteLine($&quot;Connection: {connection}&quot;);
        optionsBuilder.UseSqlServer(connection);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Fluent API를 사용해 CategoryName의 최대 길이를 15자로 제한합니다.
        // 이는 attribute를 적용하는 것으로도 가능합니다.
        modelBuilder.Entity&amp;lt;Category&amp;gt;()
        .Property(category =&amp;gt; category.CategoryName)
        .IsRequired() // NOT NULL
        .HasMaxLength(15);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) dotnet-ef 도구 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dotnet이라는 .NET CLI 도구는 EF Core사용에 유용한 기능으로 확장될 수 있습니다. 이 기능은 이전 model에서 새로운 model로 migration을 생성하고 적용하는 것과 같은 design-time작업을 수행할 수 있으며 기존 database로부터 code를 생성할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dotnet ef 명령줄 도구는 자동적으로 설치되지 않으므로 global이나 local도구를 통해 package를 설치해야 합니다. 만약 해당 도구의 이전 version이 이미 설치된 경우라면 기존 version을 모두 삭제해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terminal에서 우선 dotnet-ef가 global도구로 설치되어 있는지를 확인합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool list --global&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgVdtv/btsFh8FtT0R/ON3wl0YP52RlMIjgaKy860/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgVdtv/btsFh8FtT0R/ON3wl0YP52RlMIjgaKy860/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgVdtv/btsFh8FtT0R/ON3wl0YP52RlMIjgaKy860/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgVdtv%2FbtsFh8FtT0R%2FON3wl0YP52RlMIjgaKy860%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1155&quot; height=&quot;506&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 이전 version이 이미 설치되어 있다면 아래와 같은 방식으로 도구를 제거할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool uninstall --global dotnet-ef&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y2DDg/btsFiuayCfo/XqOXPVSdWdmsKHl7FW1i4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y2DDg/btsFiuayCfo/XqOXPVSdWdmsKHl7FW1i4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y2DDg/btsFiuayCfo/XqOXPVSdWdmsKHl7FW1i4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy2DDg%2FbtsFiuayCfo%2FXqOXPVSdWdmsKHl7FW1i4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1155&quot; height=&quot;90&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 화면상으로 version은 7.0.4인데 아래 명령을 통해 최신 version으로 update할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool update --global dotnet-ef&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치는 uninstall대신 install을 사용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool install --global dotnet-ef&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A6nfQ/btsFkGVQAsy/SJQiRL9pravaOlhLLLJVEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A6nfQ/btsFkGVQAsy/SJQiRL9pravaOlhLLLJVEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A6nfQ/btsFkGVQAsy/SJQiRL9pravaOlhLLLJVEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA6nfQ%2FbtsFkGVQAsy%2FSJQiRL9pravaOlhLLLJVEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1155&quot; height=&quot;106&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 OS별 지침에 따라 PATH 환경 변수에 dotnet tools directory를 추가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 기존 database를 사용한 Scaffolding model&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 database에서 model을 표현하기 위한 class를 생성하는 것을 reverse engineering이라고 하며 이때 도구를 사용하는 처리를 Scaffolding이라고 합니다. 좋은 Scaffolding 도구는 자동적으로 생성된 class를 확장하고 확장된 class의 손실 없이 이들 class를 재생성할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구를 사용해 class를 재생성할 일이 없다고 하더라도 원하는 만큼 자동적으로 생성된 class에서 code를 자유롭게 변경할 수 있습니다. 도구에 의해 생성된 code는 그저 최상의 근사치일 뿐입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;도구를 사용하지 않고도 최상의 결과를 얻을 수 있다면 굳이 도구사용을 시도할 필요는 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 수동적으로 생성했던 것처럼 도구가 같은 model을 생성하는지 확인해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 WorkingWithEFCore project에서 최신의 Microsoft.EntityFrameworkCore.Design package를 설치하고 project를 build 합니다. 그런 뒤 terminal에서 WorkingWithEFCore folder(csproj project file이 있는)로 찾아 들어가고 아래 명령을 통해 AutoGenModels라는 folder에 Categories와 Products에 대한 model을 생성하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet ef dbcontext scaffold &quot;Server=localhost;user id=sa;password=!123;Database=Northwind;TrustServerCertificate=true&quot; Microsoft.EntityFrameworkCore.SqlServer --table Categories --table Products --output-dir AutoGenModels --namespace WorkingWithEFCore.AutoGen --data-annotations --context Northwind&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 명령줄에 사용된 것 중 server주소와 id 및 암호는 귀하의 server에 따라 다릅니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령줄에 사용된 각 항목의 의미는 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령어는 dbcontext scaffold입니다.&lt;/li&gt;
&lt;li&gt;연결문자열은 Server=localhost;user id=sa;password=!123;Database=Northwind;TrustServerCertificate=true 입니다.&lt;/li&gt;
&lt;li&gt;Database 공급자는 Microsoft.EntityFrameworkCore.SqlServer 입니다.&lt;/li&gt;
&lt;li&gt;Model로 생성하기 위한 table은 --table Categories --table Products로 지정합니다.&lt;/li&gt;
&lt;li&gt;Model은 --output-dir AutoGenModels로 지정한 경로에 생성합니다.&lt;/li&gt;
&lt;li&gt;Model의 namespace는 --namespace WorkingWithEFCore로 지정합니다.&lt;/li&gt;
&lt;li&gt;Fluent API와 data annotation 사용을 위해 --data-annotations를 지정합니다.&lt;/li&gt;
&lt;li&gt;context명칭을 --context Northwind로 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령을 실행하면 정상적으로 처리되는 경우 아래와 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bssmEl/btsFitQbtBz/XT066DsBO8H9YTEKoUQzP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bssmEl/btsFitQbtBz/XT066DsBO8H9YTEKoUQzP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bssmEl/btsFitQbtBz/XT066DsBO8H9YTEKoUQzP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbssmEl%2FbtsFitQbtBz%2FXT066DsBO8H9YTEKoUQzP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1155&quot; height=&quot;186&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성이 완료되면 AutoGenModels folder를 열어 아래 3개 file이 생성되어 있는지를 확인합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Northwind.cs&lt;/li&gt;
&lt;li&gt;Product.cs&lt;/li&gt;
&lt;li&gt;Category.cs&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중에서 Category file을 열어 이전에 수동으로 작성한 file과의 차이점을 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689839188515&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

namespace WorkingWithEFCore.AutoGen;

[Index(&quot;CategoryName&quot;, Name = &quot;CategoryName&quot;)]
public partial class Category
{
    [Key]
    public int CategoryId { get; set; }

    [StringLength(15)]
    public string CategoryName { get; set; } = null!;

    [Column(TypeName = &quot;ntext&quot;)]
    public string? Description { get; set; }

    [Column(TypeName = &quot;image&quot;)]
    public byte[]? Picture { get; set; }

    [InverseProperty(&quot;Category&quot;)]
    public virtual ICollection&amp;lt;Product&amp;gt; Products { get; set; } = new List&amp;lt;Product&amp;gt;();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Entity class에는 [Index] attribute가 적용되어 있습니다. 이는 EF Core 5.0에서 도입된 것으로 index가 있어야 하는 속성을 나타냅니다. 이전 version에서는 단지 Fluent API를 통해서만 index를 정의할 수 있었습니다. 기존 database로의 작업에서는 이것이 필요하지 않았지만 code를 통해 초기 database를 재생성하고자 한다면 해당 정보가 필요할 것입니다.&lt;/li&gt;
&lt;li&gt;Database에서 table의 이름은 Categories이지만 dotnet-ef 도구는 Humanizer third-party library를 사용하여 class의 이름을 Category로 단수화 하였습니다. 이는 단일 entity를 생성할 때 이름을 더욱 자연스럽게 만들기 위한 것입니다.&lt;/li&gt;
&lt;li&gt;Entity class는 partial keyword를 사용하여 선언되었으므로 여기에 추가적인 code가 필요할 때 일치하는 partial class를 생성할 수 있습니다. 이를 통해 신규로 추가한 code의 손실 없이 도구를 사용해 entity class를 재생성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;CategoryId속성은 [Key] attribute가 적용되었습니다. 이는 해당 entity가 primary key임을 나타냅니다. 속성의 data type은 SQL Server에서의 int입니다.&lt;/li&gt;
&lt;li&gt;Products 속성은 [InverseProperty] attribute를 사용하여 Product entity class상에서 Category 속성으로 외래 key의 관계를 정의하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Product.cs file도 열어 이전에 수동적으로 생성한 것과의 차이를 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind.cs file도 역시 이전에 수동적으로 생성한 것과의 차이를 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689841219409&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace WorkingWithEFCore.AutoGen;

public partial class Northwind : DbContext
{
    public Northwind()
    {
    }

    public Northwind(DbContextOptions&amp;lt;Northwind&amp;gt; options)
        : base(options)
    {
    }

    public virtual DbSet&amp;lt;Category&amp;gt; Categories { get; set; }

    public virtual DbSet&amp;lt;Product&amp;gt; Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
        =&amp;gt; optionsBuilder.UseSqlServer(&quot;Server=localhost;user id=sa;password=!123;Database=Northwind;TrustServerCertificate=true&quot;);

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;
        {
            entity.Property(e =&amp;gt; e.ReorderLevel).HasDefaultValueSql(&quot;((0))&quot;);
            entity.Property(e =&amp;gt; e.UnitPrice).HasDefaultValueSql(&quot;((0))&quot;);
            entity.Property(e =&amp;gt; e.UnitsInStock).HasDefaultValueSql(&quot;((0))&quot;);
            entity.Property(e =&amp;gt; e.UnitsOnOrder).HasDefaultValueSql(&quot;((0))&quot;);

            entity.HasOne(d =&amp;gt; d.Category).WithMany(p =&amp;gt; p.Products).HasConstraintName(&quot;FK_Products_Categories&quot;);
        });

        OnModelCreatingPartial(modelBuilder);
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Northwind data context class는 parital이므로 code를 확장하고 이후에 재생성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;위의 결과를 보면 2개의 생성자를 갖고 있음을 알 수 있는데 매개변수가 없는 기본 생성자와 options 매개변수를 가진 생성자가 그것입니다. 이는 runtime에서 연결문자열을 지정하고자 할 때 유용하게 사용될 수 있습니다.&lt;/li&gt;
&lt;li&gt;위 결과에는 나타나지 않았지만 Categories와 Products table을 나타내는 2개의 DbSet&amp;lt;T&amp;gt;속성이 null-forgiving연산자값으로 설정될 수 있습니다. 이는 static compiler analysis가 compile time에서 경고가 발생되는 것을 방지하기 위함이며 runtime에서는 영향을 주지 않습니다.&lt;/li&gt;
&lt;li&gt;위 결과에는 나타나지 않았지만 OnConfiguring method안에서는 options가 생성자에서 지정되지 않은 경우를 대비한 다음과 같은 code가 만들어질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1689843874891&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    {
        //#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
        optionsBuilder.UseSqlServer(&quot;Server=localhost;user id=sa;password=!123;Database=Northwind;TrustServerCertificate=true&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;또한 연결 문자열에 민감한 정보를 hardcode 하지 말 것을 알리기 위한 compiler 경고를 포함하고 있습니다.&lt;/li&gt;
&lt;li&gt;OnModelCreating method안에서는 Fluent API가 Product에 대한 entity class를 구성하기 위해 사용되었으며 OnModelCreatingPartial이름의 partial method를 호출합니다. 이를 통해 별도의 Fluent API configuration을 추가하기 위해 partial Northwind class를 따로 만들어&amp;nbsp; partial method method를 구현할 수 있고 model class를 재생성한다고 하더라도 추가한 code에는 영향을 주지 않을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Reverse engineering template 사용자 정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 7에서 새롭게 도입된 기능 중 하나이며 dotnet-ef scaffolding tool에 의해 자동적으로 생성되는 code를 개별적으로 정의할 수 있습니다. 해당 내용에 관해서는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli&quot;&gt;Custom Reverse Engineering Templates - EF Core | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689904937614&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Custom Reverse Engineering Templates - EF Core&quot; data-og-description=&quot;Using T4 text templates to customize the scaffolded code when reverse engineering an Entity Framework Core model from a database&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cX7kiV/hyTnNTzdy3/CRZpdcqIs1rVCx4SQeRQtK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cX7kiV/hyTnNTzdy3/CRZpdcqIs1rVCx4SQeRQtK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Custom Reverse Engineering Templates - EF Core&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Using T4 text templates to customize the scaffolded code when reverse engineering an Entity Framework Core model from a database&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) 사전 규칙 model 구성하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity type과 그들의 속성에 의존하는 규칙에서 model이 더 복잡해질수록 이들을 table과 column에 성공적으로 연결시키기는 더욱 어려워집니다. 따라서 model을 분석하고 생성하기 전에 스스로에 대한 규칙을 정의할 수 있다면 해당 기능은 매우 유용하게 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 모든 string 속성은 최대 문자길이가 기본적으로 50까지만 허용된다거나 사용자 interface를 구현하는 모든 속성은 연결에서 제외되어야 한다는 규칙을 정의하고자 한다면 다음과 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689908474121&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Properties&amp;lt;string&amp;gt;().HaveMaxLength(50);
    configurationBuilder.IgnoreAny&amp;lt;IUserInterface&amp;gt;();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. EF Core model 질의하기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정을 통해 Northwind database와 2개의 table에 연결되는 model을 만들게 되었습니다. 이로서 이제는 해당 table에서 data를 가져오기 위해 LINQ query를 작성할 수 있습니다. LINQ query를 작성하는 자세한 사항은 추후에 알아보기로 하고 이번예제에서는 간단하게만 사용해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project에 Queries.cs class file을 추가하고 그 안에 QueryingCategories method와 함께 partial Program class를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689924838446&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
    static void QueryingCategories()
    {
        using (Northwind db = new())
        {
            IQueryable&amp;lt;Category&amp;gt;? categories = db.Categories?.Include(c =&amp;gt; c.Products);

            if ((categories is null) || (!categories.Any()))
            {
                Console.WriteLine(&quot;Failed&quot;);
                return;
            }
                
            foreach (Category c in categories)
            {
                Console.WriteLine($&quot;{c.CategoryName} has {c.Products.Count} products.&quot;);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 다음 사항에 주목합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제에서는 database를 관리할 Northwind class의 instance를 생성하고 있습니다. Database context instance는 작업단위 짧은 생명주기를 위해 설계되었으며 가능한 한 빨리 dispose 되어야 하므로 using문을 사용하였습니다.&lt;/li&gt;
&lt;li&gt;관련된 projects를 포함한 전체 category를 위한 query를 생성합니다. 여기서 Include는 확장 method이며&amp;nbsp; 사용을 위해서는 Microsoft.EntityFrameworkCore namespace를 import 해야 합니다.&lt;/li&gt;
&lt;li&gt;가져온 category를 열거하면서 해당 category의 이름과 포함된 project의 수를 표시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;if 문에서 || 사이의 순서는 중요한 부분입니다. categories가 null인지를 우선적으로 확인해야 하는데 만약 그 결과가 true라면 두 번째 문을 실행되지 않을 것이며 따라서 Any() member에 접근할 때 NullReferenceException 예외가 발생하지 않을 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 Northwind instance를 생성하고 database provider의 이름을 출력하는 두 문을 주석처리하고 QueryingCategories method를 아래와 같이 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689925921829&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;QueryingCategories();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCTY3y/btsFjIsPHRj/kJKQ379z6mXJ7skWcELGW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCTY3y/btsFjIsPHRj/kJKQ379z6mXJ7skWcELGW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCTY3y/btsFjIsPHRj/kJKQ379z6mXJ7skWcELGW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCTY3y%2FbtsFjIsPHRj%2FkJKQ379z6mXJ7skWcELGW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;186&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 포함된 entity filtering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 5에서는 filtered include를 도입하여 결과에서 반환된 entity를 filter 하기 위해 호출하는 Include method안에서 lambda 식을 명시할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queries.cs에서 FilteredIncludes method를 아래와 같이 정의하고 아래 작업을 수행하는 문을 추가합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database를 다룰 Northwind class의 instance를 생성합니다.&lt;/li&gt;
&lt;li&gt;사용자에게 재고의 최솟값을 입력받을 수 있도록 요청합니다.&lt;/li&gt;
&lt;li&gt;재고가 입력된 값보다 같거나 그 보다 많은 product를 가진 Category를 가져오기 위해 query를 생성합니다.&lt;/li&gt;
&lt;li&gt;Category와 Product를 열거하면서 각각에 대한 이름과 재고를 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1689926879027&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void FilteredIncludes()
{
    using (Northwind db = new())
    {
        string? input;
        int stock;

        do
        {
            Console.Write(&quot;Enter a minimum for units in stock: &quot;);
            input = Console.ReadLine();

        } while (!int.TryParse(input, out stock));

        IQueryable&amp;lt;Category&amp;gt;? categories = db.Categories?.Include(c =&amp;gt; c.Products.Where(p =&amp;gt; p.Stock &amp;gt;= stock));

        if ((categories is null) || (!categories.Any()))
        {
            Console.WriteLine(&quot;No categories found.&quot;);
            return;
        }

        foreach (Category c in categories)
        {
            Console.WriteLine($&quot;{c.CategoryName} has {c.Products.Count} products with a minimum of {stock} units in stock.&quot;);

            foreach (Product p in c.Products)
            {
                Console.WriteLine($&quot;{p.ProductName} has {p.Stock} units in stock.&quot;);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 위의 FilteredIncludes method를 호출하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFcHxB/btsFm4uWye5/v2WOn5zklgMsSEjDd6bYZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFcHxB/btsFm4uWye5/v2WOn5zklgMsSEjDd6bYZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFcHxB/btsFm4uWye5/v2WOn5zklgMsSEjDd6bYZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFcHxB%2FbtsFm4uWye5%2Fv2WOn5zklgMsSEjDd6bYZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;350&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 10 Fall Creators Update이전 Windows version에서 제공하는 console에는 하나의 제한사항이 있습니다. 기본적으로 console은 Unicode문자를 표시할 수 없습니다. 만약 예제를 실행하는 computer가 그렇다면 임시로 character set으로 알려진 code page를 console에서 아래 명령으로 Unicode UTF-8로 변경함으로써 해결할 수 있습니다.(예제를 실행시키기 전에)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;chcp 65001&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 source에서 unicode문자를 표시하기전 아래 문을 넣어두면 정상적으로 unicode문자를 표시할 수 있게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Console.OutputEncoding&amp;nbsp;=&amp;nbsp;System.Text.Encoding.UTF8;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Product filtering 하고 sorting 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data를 filtering 하고 sorting 하기 위해 Queries.cs에서 QueryingProducts method를 정의하고 그 안에 &amp;nbsp;아래의 동작을 위한 구문을 추가합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database를 다룰 Northwind class의 instance를 생성합니다.&lt;/li&gt;
&lt;li&gt;사용자에게 products에 대한 가격을 입력하도록 요청합니다.&lt;/li&gt;
&lt;li&gt;LINQ를 통해 입력한 가격보다 더 높은 products를 가져오는 query를 생성합니다.&lt;/li&gt;
&lt;li&gt;결과를 순회하면서 ID, name, cost(예제에서의 단위는 dollar입니다.)와 재고를 표시하도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690026085470&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void QueryingProducts()
{
    using (Northwind db = new())
    {
        string? input;
        decimal price;
        do
        {
            Console.Write(&quot;Enter a product price: &quot;);
            input = Console.ReadLine();
        } while (!decimal.TryParse(input, out price));

		IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(product =&amp;gt; product.Cost &amp;gt; price).OrderByDescending(product =&amp;gt; product.Cost);

		if ((products is null) || (!products.Any()))
		{
			Console.Write(&quot;No products found.&quot;);
			return;
		}

		foreach (Product p in products)
		{
			Console.WriteLine(&quot;{0}: {1} costs {2:$#,##0.00} and has {3} in stock.&quot;, p.ProductId, p.ProductName, p.Cost, p.Stock);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서의 '!products.Any()'는 'products.Count() == 0'과 같이 count를 확인하는 것보다 더 효휼적으로 작동합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 QueryingProducts method를 호출하면 사용자에게 price입력을 요청하고 그 결과를 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kMHBO/btsFiN2ca22/AjQkrmjzOKgnhWKuA9P4Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kMHBO/btsFiN2ca22/AjQkrmjzOKgnhWKuA9P4Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kMHBO/btsFiN2ca22/AjQkrmjzOKgnhWKuA9P4Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkMHBO%2FbtsFiN2ca22%2FAjQkrmjzOKgnhWKuA9P4Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;229&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위 입력요청에서 price를 500과 같이 입력하면 결과는 다음처음 달라질 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctSZMQ/btsFmjeLAMn/mNJoi6KG1ZzlR3w0kLgkV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctSZMQ/btsFmjeLAMn/mNJoi6KG1ZzlR3w0kLgkV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctSZMQ/btsFmjeLAMn/mNJoi6KG1ZzlR3w0kLgkV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctSZMQ%2FbtsFmjeLAMn%2FmNJoi6KG1ZzlR3w0kLgkV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 생성된 SQL 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상황에서 C# code가 실제 어떤 SQL문을 생성하는지 궁금할 수 있습니다. EF Core 5부터는 생성된 SQL문을 보기 위한 빠르고 편리한 다음 방법을 도입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FilteredIncludes method에서 foreach문을 사용해 query를 열거하기 전에 생성된 SQL문을 출력하는 다음 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690098287132&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if ((categories is null) || (!categories.Any()))
{
    Console.WriteLine(&quot;No categories found.&quot;);
    return;
}

Console.WriteLine($&quot;ToQueryString: {categories.ToQueryString()}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같이 생성된 SQL문을 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9eRBU/btsFoeYhV4j/xMLmiN1hjvgb13ypoGh341/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9eRBU/btsFoeYhV4j/xMLmiN1hjvgb13ypoGh341/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9eRBU/btsFoeYhV4j/xMLmiN1hjvgb13ypoGh341/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9eRBU%2FbtsFoeYhV4j%2FxMLmiN1hjvgb13ypoGh341%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;590&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) EF Core Logging&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core와 database간상호작용을 살펴보기 위해 logging을 사용할 수 있습니다. 이때 logging은 console이나 Debug, Trace 또는 file이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 EF Core logging은 민감한 data는 제외합니다. 만약 이러한 data도 모두 포함하고자 한다면(특히 개발과정 동안에는) EnableSensitiveDataLogging method를 호출할 수 있습니다. 반대로 완성된 Application을 출시하는 경우 해당 기능을 disable 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind.cs의 OnConfiguring method아래에 console로의 log를 위한 아래 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690099001626&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;optionsBuilder.UseSqlServer(connection);
optionsBuilder.LogTo(Console.WriteLine).EnableSensitiveDataLogging();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LogTo는 Action&amp;lt;string&amp;gt; delegate를 필요로 하며 EF core는 각 log message에 대한 문자열값을 전달함으로써 해당 delegate를 호출할 것입니다. 따라서 위 예제처럼 Console class의 WriteLine method를 전달하는 것은 logger에게 각 method를 console에 쓰도록 하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같이 각 log를 상세하게 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;1847&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzCzoa/btsFobUQwQo/L6EHaPFJtNg42IF07Tnc51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzCzoa/btsFobUQwQo/L6EHaPFJtNg42IF07Tnc51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzCzoa/btsFobUQwQo/L6EHaPFJtNg42IF07Tnc51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzCzoa%2FbtsFobUQwQo%2FL6EHaPFJtNg42IF07Tnc51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;1847&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;1847&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Log는 사용 중인 database 공급자와 code 편집기 및 향후 EF Core의 개선점에 따라 달라질 수 있습니다. 일반적으로 연결을 열거나 명령 실행과 같은 다른 event마다 다음과 같이 다른 event ID를 가지고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RelationalEventId.ConnectionOpening[20000] : 원격 server 혹은 file 경로를 포함&lt;/li&gt;
&lt;li&gt;RelationalEventId.ConnectionOpened[20001] : 원격 server 혹은 file 경로를 포함&lt;/li&gt;
&lt;li&gt;RelationalEventId.CommandExecuting[20100] : 실행하는 SQL문 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 공급자별 값을 통한 log filtering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event ID값과 그 의미는 EF Core 공급자에 따라 달라질 수 있습니다. LINQ query가 SQL 문으로 어떻게 변환되고 실행되는지를 알고자 한다면 20100의 Id값을 가진 event ID를 확인하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 아래에서와 같이 Northwind.cs에서 LogTo method를 변경하여 Id가 20100인 event만 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690162401806&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuting }).EnableSensitiveDataLogging();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같이 log 된 SQL문을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5djcB/btsFlQcS3fz/LgM9FfwkcreFurb0kbMCwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5djcB/btsFlQcS3fz/LgM9FfwkcreFurb0kbMCwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5djcB/btsFlQcS3fz/LgM9FfwkcreFurb0kbMCwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5djcB%2FbtsFlQcS3fz%2FLgM9FfwkcreFurb0kbMCwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;594&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Query tag를 통한 logging&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ query를 logging 할 때 복잡한 상황에서 log message를 서로 관련시키기가 어려울 수 있습니다. EF Core 2.2에서는 query tag 기능을 도입하여 SQL 문을 log에 추가시키기 한결 편리해졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 TagWith method를 사용해 LINQ query에 적용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690165111762&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IQueryable&amp;lt;Category&amp;gt;? categories = db.Categories?.TagWith(&quot;*****Category*****&quot;).Include(c =&amp;gt; c.Products.Where(p =&amp;gt; p.Stock &amp;gt;= stock));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 log에 SQL comment를 다음과 같이 추가하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t1JW1/btsFoe46Ng3/bzyvqJkkHcVPDkh4dxQa10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t1JW1/btsFoe46Ng3/bzyvqJkkHcVPDkh4dxQa10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t1JW1/btsFoe46Ng3/bzyvqJkkHcVPDkh4dxQa10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft1JW1%2FbtsFoe46Ng3%2FbzyvqJkkHcVPDkh4dxQa10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Like를 사용한 pattern matching&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core에서는 pattern matching을 위한 Like와 같이 일반적인 SQL문을 지원하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queries.cs에서 다음과 같이 QueryingWithLike이름의 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690165447737&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void QueryingWithLike()
{
    using (Northwind db = new())
    {
        Console.Write(&quot;Enter part of a product name: &quot;);
        string? input = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(input))
        {
            Console.WriteLine(&quot;You did not enter part of a product name.&quot;);
            return;
        }

        IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(p =&amp;gt; EF.Functions.Like(p.ProductName, $&quot;%{input}%&quot;));

        if ((products is null) || (!products.Any()))
        {
            Console.WriteLine(&quot;No products found.&quot;);
            return;
        }

        foreach (Product p in products)
        {
            Console.WriteLine(&quot;{0} has {1} units in stock. Discontinued? {2}&quot;, p.ProductName, p.Stock, p.Discontinued);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 logging을 사용하고 있으며 사용자에게 product에 대한 이름 중 일부에 대한 입력을 요구하고 있습니다. 사용자가 입력을 완료하면 EF.Functions.Like method를 사용해 입력된 내용을 ProductName 속성으로 검색을 시도합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 일치하는 product가 존재하면 각 product에 대한 이름과 재고 그리고 생산여부등을 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 기존의 method 호출을 주석처리하고 위에서 추가한 QueryingWithLike method를 호출하도록 합니다. 그런 뒤 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUcf2i/btsFirLC1aW/KsNgxCfGLQFsJYRgTKDDh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUcf2i/btsFirLC1aW/KsNgxCfGLQFsJYRgTKDDh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUcf2i/btsFirLC1aW/KsNgxCfGLQFsJYRgTKDDh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUcf2i%2FbtsFirLC1aW%2FKsNgxCfGLQFsJYRgTKDDh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;396&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Query를 통한 임의 숫자 생성하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 6에서는 한 가지 유용한 기능인 EF.Functions.Random을 도입하여 0과 1 사이에 배타적인 의사난수를 반환하는 database기능과 연결할 수 있습니다. 예를 들어 table에서 임의의 row를 선택하기 위해 전체 row 수에서 임의의 숫자를 곱할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queries.cs에서 GetRandomProduct이름의 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690179811322&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void GetRandomProduct()
{
    using (Northwind db = new())
    {
        int? rowCount = db.Products?.Count();

        if (rowCount == null)
        {
            Console.WriteLine(&quot;Products table is empty.&quot;);
            return;
        }

        Product? p = db.Products?.FirstOrDefault(p =&amp;gt; p.ProductId == (int)(EF.Functions.Random() * rowCount));

        if (p == null)
        {
            Console.WriteLine(&quot;Product not found.&quot;);
            return;
        }

        Console.WriteLine($&quot;Random product: {p.ProductId} {p.ProductName}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 GetRandomProduct method를 호출하면 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHbCwC/btsFkTOnk2q/THoeojkG9NhPZmm7dUrreK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHbCwC/btsFkTOnk2q/THoeojkG9NhPZmm7dUrreK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHbCwC/btsFkTOnk2q/THoeojkG9NhPZmm7dUrreK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHbCwC%2FbtsFkTOnk2q%2FTHoeojkG9NhPZmm7dUrreK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;255&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) Global filter 정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind의 products, 즉 제품은 단종될 수 있을 것입니다. 따라서 단종된 product는 결과에 반영되지 않도록 하는 것이 합리적일 수 있는데 products를 조회하는 모든 곳에 where를 사용하여 이러한 사항을 filtering 하기보다는 global로 filter를 적용하여 where를 사용하지 않고 해당 filtering을 products를 조회하는 모든 곳에 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind.cs의 OnModelCreating method아래에 아래와 같이 단종된(discontinued) product를 제거하는 global filter를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690180571033&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;modelBuilder.Entity&amp;lt;Category&amp;gt;()
            .Property(category =&amp;gt; category.CategoryName)
            .IsRequired() // NOT NULL
            .HasMaxLength(15);
modelBuilder.Entity&amp;lt;Product&amp;gt;().HasQueryFilter(p =&amp;gt; !p.Discontinued);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 QueryingWithLike method를 호출하고 product name으로 che를 입력합니다. 다음 결과에서 보듯 'Chef Anton&amp;rsquo;s Gumbo Mix'는 반영되지 않았습니다. 생성된 SQL문을 확인해 보면 Discontinued column에 대한 filter를 포함하고 있기 때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YzEe8/btsFiOz1Eru/3KADFnNTqeCTAUhimYsy8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YzEe8/btsFiOz1Eru/3KADFnNTqeCTAUhimYsy8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YzEe8/btsFiOz1Eru/3KADFnNTqeCTAUhimYsy8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYzEe8%2FbtsFiOz1Eru%2F3KADFnNTqeCTAUhimYsy8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;389&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. EF Core의 loading pattern&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core에서 일반적으로 사용되는 3가지 loading pattern이 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eager loading : 사전 data load&lt;/li&gt;
&lt;li&gt;Lazy loading : 필요하기 직전에 자동적으로 data load&lt;/li&gt;
&lt;li&gt;Explicit loading : 수동으로 data load&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 확장 method 포함을 사용한 eager loading&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QueryingCategories method에서 현재 code는 각 category를 순회하기 위해 Categories 속성을 사용하며 category name과 category에 속한 products 수를 출력하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 이전에 query를 작성할 때 관련된 products에 대한 Include method를 호출함으로써 eager loading를 사용했기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Include를 사용하지 않으면 어떤 현상이 발생하는지 확인해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;QueryingCategories&lt;span&gt; method에서 &lt;/span&gt;&lt;/span&gt;아래와 같이 Include method의 호출 부분을 주석처리합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690189711342&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IQueryable&amp;lt;Category&amp;gt;? categories = db.Categories;//?.Include(c =&amp;gt; c.Products);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 QueryingCategories method를 호출하면 다음과 같은 결과가 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3cO2R/btsFod59xpr/i8emNIsz1LkFktfa1iakR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3cO2R/btsFod59xpr/i8emNIsz1LkFktfa1iakR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3cO2R/btsFod59xpr/i8emNIsz1LkFktfa1iakR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3cO2R%2FbtsFod59xpr%2Fi8emNIsz1LkFktfa1iakR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;376&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foreach안에서 각 item은 Products 속성을 갖고 있는 Category class의 instance인데 Products 속성은 category에서의 products list를 의미합니다. 위 예제에서 query는 단지 Categories table만을 select 하고 있으므로 해당 속성은 각 category에서 비어있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Lazy loading 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lazy loading은 EF Core 2.1에서 도입된 것으로 누락된 관계 data를 자동으로 load 할 수 있습니다. lazy loading을 사용하기 위해서는 우선 proxy를 위한 NuGet package를 참조해야 하며 해당 proxy사용을 위한 lazy loading을 구성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022에서 Microsoft.EntityFrameworkCore.Proxies NuGet package를 찾아 이를 참조합니다. 그리고 Northwind.cs의 OnConfiguring method아래에 UseLazyLoadingProxies 확장 method를 호출하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690251859853&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuting }).EnableSensitiveDataLogging();
optionsBuilder.UseLazyLoadingProxies();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 loop를 열거하면서 Products 속성을 읽으려고 시도할 때마다 lazy loading proxy는 data가 load 되었는지를 확인합니다. 그렇지 않다면 SELECT문을 실행하여 현재 category에 대한 products만을 load 하게 됩니다. 다시 말해 속성에 대한 접근이 있으면 그때 data를 database server에서 가져와야 할지를 판단하고 필요하면 그렇게 한다는 것입니다. 이렇게 되면 정확한 count를 출력에 반환할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 다시 실행하면 이번에는 정확한 product count가 표시될 것입니다. 다만 모든 data를 가져오기 위해 database server와 여러 번 통신해야 하는 과정이 필요하다는 단점이 있습니다. 예를 들어 모든 categories를 가져온 다음 첫 번째 category인 Beverages에 대한 products를 가져오기 위해 아래와 같이 두 번의 SQL 명령을 실행하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkqQu4/btsFkKDZyEK/aOBTupt7mkAPNJfvSZNzFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkqQu4/btsFkKDZyEK/aOBTupt7mkAPNJfvSZNzFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkqQu4/btsFkKDZyEK/aOBTupt7mkAPNJfvSZNzFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkqQu4%2FbtsFkKDZyEK%2FaOBTupt7mkAPNJfvSZNzFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;407&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Load method를 사용해 명시적으로 entity loading 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적 loading은 lazy loading과 비슷하지만 관계 data가 load 되는 것과 언제 load 될지를 정확히 제어한다는 차이가 있습니다. lazy loading은 해당 속성에 접근하면 처리되지만 explicit loading은 처리시점과 대상을 개발자가 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queries.cs에서 우선 ChangeTracking namespace를 import 합니다. 해당 namespace는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;관계 entity를 수동적으로 load 하도록 하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;CollectionEntry class를 사용하기 위한 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690253693570&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore.ChangeTracking;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QueryingCategories안에서 아래와 같이 lazy loading을 위해 변경한 부분을 주석처리한 다음 사용자에게 eager loading과 explicit loading 중 어느 것을 사용할지를 선택하도록 요청하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690253952986&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IQueryable&amp;lt;Category&amp;gt;? categories;// = db.Categories;//?.Include(c =&amp;gt; c.Products);

db.ChangeTracker.LazyLoadingEnabled = false;

Console.Write(&quot;Enable eager loading? (Y/N): &quot;);
bool eagerLoading = (Console.ReadKey(intercept: true).Key == ConsoleKey.Y);
bool explicitLoading = false;
Console.WriteLine();

if (eagerLoading)
{
    categories = db.Categories?.Include(c =&amp;gt; c.Products);
}
else
{
    categories = db.Categories;
    Console.Write(&quot;Enable explicit loading? (Y/N): &quot;);
    explicitLoading = (Console.ReadKey(intercept: true).Key == ConsoleKey.Y);
    Console.WriteLine();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foreach loop안에서는 WriteLine method를 호출하기 전 explicit loading을 사용하는지 확인하고 그렇다면 사용자에게 명시적으로 각각의 개별 category를 명시적으로 load 할지를 결정하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 eager loading를 비활성화하기 위해 N을 누릅니다. 그런 다음 explicit loading을 위해 Y를 누르고 각 category에서 products를 load 할지에 대한 선택을 진행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdg0mW/btsFh68K0Ca/ei3GM7Y9WmAJte1VhKy65K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdg0mW/btsFh68K0Ca/ei3GM7Y9WmAJte1VhKy65K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdg0mW/btsFh68K0Ca/ei3GM7Y9WmAJte1VhKy65K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdg0mW%2FbtsFh68K0Ca%2Fei3GM7Y9WmAJte1VhKy65K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 처음 2건에 대한 products만을 load 하도록 하였습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사용하고자 하는 loading pattern을 선택할 때는 주의해야 합니다. loading pattern에 관해서는 아래 link를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Entity 추적 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 entity의 identity resolution을 정의하는 것으로 시작해야 합니다. EF Core는 entity instance에서 각각의 고유한 primary key값을 읽음으로서 resolve를 수행하는데 이런 과정을 통해 entity 혹은 entity 사이의 관계를 식별하는데 모호함을 제거할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core는 기본적으로 appliction이 실행되는 memory안에서 entity를 추적합니다. 따라서 새로운 entity를 추가하거나 수정하거나 삭제하는등의 변경사항이 발생하면 꼭 SaveChanges method를 호출하여 해당 변경사항이 실제 database에 반영되도록 해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;EF Core는 key가 database에서 entity를 식별할 수 있는 고유값에 해당하므로 이를 통해서만 entity를 추적할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind database에서 Customer table을 보면 여기에 다음과 같은 고객 정보가 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;CustomerId: ALFKI&lt;br /&gt;CompanyName: Alfreds Futterkiste&lt;br /&gt;Country:Germany&lt;br /&gt;Phone: 030-0074321&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 해당 data context에서 Germany에 해당하는 모든 customer를 불러오는 query를 실행한 뒤 같은 data context안에서 이름이 A로 시작하는 모든 customer를 불러오는 또 다른 query를 실행한다면, 해당 context안에서 특정 customer가 이미 존재한다면 성능향상을 이는 다시 load하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이전에 위 설명과 같은 2개의 query를 실행하는 사이 customer의 telephone정보가 update됐다면 data context에서 추적된 entity는 변경된 telephone 정보를 반영하지 않게 되므로 이를 최신의 customer정보로 인정할 수 없게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 지역적 변경사항에 대한 추적이 필요하지 않거나 매번 query를 실행할때 마다 최신의 data값을 가진 entity의 instance를 새롭게 load하고자 한다면 추적기능 자체를 비활성화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 query에서 추적을 끄려면 해당 query에서 AsNoTracking method를 호출하거나&lt;/p&gt;
&lt;pre id=&quot;code_1709694982788&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var products = db.Products
 .AsNoTracking()
 .Where(p =&amp;gt; p.UnitPrice &amp;gt; price)
 .Select(p =&amp;gt; new { p.ProductId, p.ProductName, p.UnitPrice });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data context의 instance자체에서 추적을 끄려면 ChangeTracker의 QueryTrackingBehavior설정을 아래와 같이 NoTracking으로 설정해 주면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709695092195&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 각각의 query에서 추적을 끄지만 identity resolution을 그대로 유지하고자 한다면 AsNoTrackingWithIdentityResolution method를 대신 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709695187044&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var products = db.Products
 .AsNoTrackingWithIdentityResolution()
 .Where(p =&amp;gt; p.UnitPrice &amp;gt; price)
 .Select(p =&amp;gt; new { p.ProductId, p.ProductName, p.UnitPrice });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 같은 원리로 data context의 instance에도 같은 설정을 적용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709695277657&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTrackingWithIdentityResolution;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 새로운 data context에서 일관되게 같은 설정을 적용해야 한다면 OnConfiguring method에서 UseQueryTrackingBehavior method를 호출해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709695370667&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(connectionString).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 추적이 발생하는 3가지 사례&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째로 identity resolution을 통한 default tracking을 사용하는 경우 입니다. Entity가 일단 data context로 load되면 실제 database에 대한 변경사항은 적용되지 않으며 지역적으로 하나의 복사본이 생성됩니다. Entity는 지역적으로 변경된 사항을 추적하고 SaveChanges method를 호출하여 database를 update합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;동작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database Table Row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SaveChanges&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째는 변경사항에 대한 추적을 하지 않고 identity resolution을 사용하지 않는 경우 입니다. 모든 질의는 다른 table row의 instance를 load하며 변경과 중복을 허용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;동작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database Table Row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;br /&gt;B&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;SaveChanges&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;br /&gt;B&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 세번째는 변경사항에 대한 추적을 하지 않으면서 identity resolution을 사용하는 경우입니다. 일단 entity가 data context로 load되고 나면 Database에 대한 변경사항은 적용되지 않으며 오로지 복사본만 존재합니다. 변경사항을 추적하지 않으므로 두번째 경우와 마찬가지로 SaveChanges method는 어떠한 동작도 수행하지 않습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;동작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database Table Row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;SaveChanges&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 추적없는 query에 대한 Lazy loading &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 7과 그 이전에서는 추적을 사용하지 않도록 설정하면 lazy loading pattern은 사용할 수 없었고 만약 그러한 시도를 하게 되면 System.InvalidOperationException예외가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 EF Core 8에 들어서 추적을 활성화하지 않더라도 entity에 대한 lazy loading을 지원하기 시작했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709707838666&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static void LazyLoadingWithNoTracking()
{
    using NorthwindDb db = new();
 
    IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.AsNoTracking();
    
    if (products is null || !products.Any())
    {
        Console.WriteLine(&quot;No Products.&quot;);
        return;
    }

    foreach (Product p in products)
    {
        Console.WriteLine(&quot;{0} is in category {1}.&quot;, p.ProductName, p.Category.CategoryName);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 Product에 대한 query에서 추적을 사용하지 않도록 하고 있으며 Product를 열거할때 lazy loading을 사용하여 관련된 Category이름을 가져오도록 하고 있습니다. EF Core 7이나 6에서 위 code를 실행하면 예외가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● AsNoTracking에 대한 오해.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷의 여러 community에서는 간혹 AsNoTracking이 성능향상을 위한 좋은 선택임을 강조하는 경우가 있습니다. 하지만 상상해 보십시오. 수십, 수백개의 entity를 가져오는 query를 AsNoTracking으로 실행한 뒤 다시 같은 data context안에서 동일한 query를 수행하게 되면 memory에는 수백개의 중복된 entity가 저장되는 상황을 맞이하게 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 memory낭비를 초래하며 당연히 성능에도 부정적인 영향을 줄 수 밖에 없을 것입니다. 다시 말해 성능향상을 위해 AsNoTracking을 호출하라는 조언은 모든 상황에서 해당되지 않을 수 있으므로 주의가 필요합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. EF Core를 사용한 data변경&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core를 사용해 entity를 Insert, update, delete 하는 데는 큰 어려움이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext는 변화에 대한 추적관리를 자동으로 하게 되므로 local entity에 새로운 entity를 추가하거나 기존 entity를 수정하거나 혹은 삭제하는 것을 포함해 추적된 여러 변화를 가질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이러한 변경된 사항들을 database에 반영하려면 SaveChanges method를 호출하기만 하면 되고 경우에 따라 성공적으로 변경된 entity의 수가 반환될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Entity Insert&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Table에 새로운 row를 추가해 보기 위해 Modifications.cs file을 추가하고 여기에 가장 비싼 순으로 정렬된 각 product에 대한 ID, name, cost, stock, 그리고 discontinued속성을 출력하고 method에 선택적으로 전달된 int값의 array와 일치하는 모든 것을 가종하는 ListProducts이름의 method와 함께 partial Program class를 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690267855914&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
    static void ListProducts(int[]? productIdsToHighlight = null)
    {
        using (Northwind db = new())
        {
            if ((db.Products is null) || (!db.Products.Any()))
            {
                Console.WriteLine(&quot;There are no products.&quot;);
                return;
            }

            Console.WriteLine(&quot;| {0,-3} | {1,-35} | {2,8} | {3,5} | {4} |&quot;, &quot;Id&quot;, &quot;Product Name&quot;, &quot;Cost&quot;, &quot;Stock&quot;, &quot;Disc.&quot;);

            foreach (Product p in db.Products)
            {
                ConsoleColor previousColor = System.Console.ForegroundColor;

                if ((productIdsToHighlight is not null) &amp;amp;&amp;amp; productIdsToHighlight.Contains(p.ProductId))
                {
                    System.Console.ForegroundColor = ConsoleColor.Green;
                }

                Console.WriteLine(&quot;| {0:000} | {1,-35} | {2,8:$#,##0.00} | {3,5} | {4} |&quot;, p.ProductId, p.ProductName, p.Cost, p.Stock, p.Discontinued);

                System.Console.ForegroundColor = previousColor;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 예제에서 '{숫자1, 숫자2}'는 숫자 1번째의 인수를 숫자 2만큼의 문자열 넓이로 정렬함을 의미합니다. 이때 숫자 2가 -면 왼쪽 정렬을 양수이면 오른쪽 정렬입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 Modifications.cs에 AddProduct method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690268164449&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static (int affected, int productId) AddProduct(int categoryId, string productName, decimal? price)
{
    using (Northwind db = new())
    {
        if (db.Products is null) return (0, 0);

        Product p = new()
        {
            CategoryId = categoryId,
            ProductName = productName,
            Cost = price,
            Stock = 72
        };

        // Insert 추가
        EntityEntry&amp;lt;Product&amp;gt; entity = db.Products.Add(p);
        Console.WriteLine($&quot;State: {entity.State}, ProductId: {p.ProductId}&quot;);

        // 변경된 추적사항을 database에 반영
        int affected = db.SaveChanges();
        Console.WriteLine($&quot;State: {entity.State}, ProductId: {p.ProductId}&quot;);

        return (affected, p.ProductId);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 주석처리하고 위의 AddProduct와 ListProducts를 호출하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690268974544&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var resultAdd = AddProduct(categoryId: 6, productName: &quot;Apple2 Computer&quot;, price: 1200M);

if (resultAdd.affected == 1)
{
    Console.WriteLine($&quot;Add product successful with ID: {resultAdd.productId}.&quot;);
}
                         
ListProducts(productIdsToHighlight: new[] { resultAdd.productId });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 새로운 product를 추가한 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A1k0W/btsFoI5UXPY/wFrTjijxPsVqcj1o0LDJ0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A1k0W/btsFoI5UXPY/wFrTjijxPsVqcj1o0LDJ0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A1k0W/btsFoI5UXPY/wFrTjijxPsVqcj1o0LDJ0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA1k0W%2FbtsFoI5UXPY%2FwFrTjijxPsVqcj1o0LDJ0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;479&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;479&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;새로운 product가 처음 memory에 생성되고 EF Core change tracker에 의해 추적되기 시작하면 Added 상태와 0인 ID를 가지게 됩니다. 그런 후 SaveChanges가 호출되면 상태는 UnChanged로 바뀌게 되고 ID값은 78로 database에 의해 할당됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Entity Update&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 존재하는 table의 row를 변경하기 위해 update 할 product를 product name의 시작을 지정하여 검색한 다음 가장 먼저 일치하는 것만 반환하도록 하는 예제를 작성할 것입니다.(실제로는 ProductId와 같이 고유한 식별자를 사용해 update할 product를 지정하는 것이 일반적입니다.)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Product를 추가할 때는 실제 어떤 product ID가 할당될지 알 수 없습니다. 다만 현재 Northwind database에&amp;nbsp; 'Apple2'로 시작하는 product가 없다는 것만 알고 있을 뿐입니다. name을 사용해 update 할 product를 찾으면 이전에 추가한 product의 ID를 알아내야 하는 것을 피할 수 있습니다. 물론 database에는 77까지의 ID가 존재하므로 새로운 product의 ID가 78이 될 수 있음을 유추할 수 있지만 product를 추가한 뒤 삭제한다고 해도 다음 product의 ID는 79가 될 수 있으며 이때부터 번호순서 예상에 맞지 않을 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifications.cs에서는 name이 지정된 값으로 시작하는 product에 대해 $20과 같이 지정한 만큼 price를 증가시키는 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690270991101&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static (int affected, int productId) IncreaseProductPrice(string productNameStartsWith, decimal amount)
{
    using (Northwind db = new())
    {
        if (db.Products is null) return (0, 0);

        Product updateProduct = db.Products.First(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

        updateProduct.Cost += amount;

        int affected = db.SaveChanges();

        return (affected, updateProduct.ProductId);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 IncreaseProductPrice method를 호출한 다음 다시 ListProducts method를 호출하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690271077782&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var resultUpdate = IncreaseProductPrice(productNameStartsWith: &quot;Apple&quot;, amount: 30M);

if (resultUpdate.affected == 1)
{
    Console.WriteLine(&quot;Increase price success for ID: {resultUpdate.productId}.&quot;);
}

ListProducts(productIdsToHighlight: new[] { resultUpdate.productId });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다. 이전에 추가한 'Apple2 Computer'에 대한 price가 $30만큼 증가되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuNkc6/btsFh4392px/8nD6zH5ScJeV7eDskHR7A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuNkc6/btsFh4392px/8nD6zH5ScJeV7eDskHR7A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuNkc6/btsFh4392px/8nD6zH5ScJeV7eDskHR7A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuNkc6%2FbtsFh4392px%2F8nD6zH5ScJeV7eDskHR7A1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Entity Delete&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 entity들은 Remove method를 사용함으로써 삭제할 수 있습니다. RemoveRange는 여러 entity를 제거하고자 할 때 더 적합한 method입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifications.cs에서 지정한 값으로 시작하는 모든 product를 삭제하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690354744991&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int DeleteProducts(string productNameStartsWith)
{
    using (Northwind db = new())
    {
        IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

        if ((products is null) || (!products.Any()))
        {
            Console.WriteLine(&quot;No products found to delete.&quot;);

            return 0;
        }
        else
        {
            if (db.Products is null) return 0;
            db.Products.RemoveRange(products);
        }

        int affected = db.SaveChanges();
        return affected;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 아래와 같이 DeleteProducts method를 호출하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690354848486&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;About to delete all products whose name starts with Apple.&quot;);
Console.Write(&quot;Press Enter to continue or any other key to exit: &quot;);

if (Console.ReadKey(intercept: true).Key == ConsoleKey.Enter)
{
    int deleted = DeleteProducts(productNameStartsWith: &quot;Apple&quot;);

    Console.WriteLine($&quot;{deleted} product(s) were deleted.&quot;);
}
else
{
    Console.WriteLine(&quot;Delete was canceled.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 Enter key를 누르면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYuV1r/btsFqaA5pwP/gCiTw1x8AMJzPiQRydG6k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYuV1r/btsFqaA5pwP/gCiTw1x8AMJzPiQRydG6k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYuV1r/btsFqaA5pwP/gCiTw1x8AMJzPiQRydG6k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYuV1r%2FbtsFqaA5pwP%2FgCiTw1x8AMJzPiQRydG6k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Apple로 시작하는 product가 여러 건이라면 해당되는 모든 product는 삭제될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 더욱 효율적인 Update와 Delete&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 방법은 EF Core를 사용한 비교적 전통적인 data수정 방식으로 과정을 다음과 같이 요약할 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Database context를 생성합니다. (변경추적은 기본으로 사용됩니다.)&lt;/li&gt;
&lt;li&gt;Insert를 위해 entity class에 대한 instance를 생성하고 Add method의 매개변수로 collection을 db.Products.Add(product) 처럼 전달합니다.&lt;/li&gt;
&lt;li&gt;Update를 위해서는 update 하고자 하는 entity를 가져와 이들에 대한 속성을 변경합니다.&lt;/li&gt;
&lt;li&gt;Delete를 위해서는 delete 하고자 하는 entity를 가져와 Remove 또는 RemoveRange method의 매개변수로 db.Products.Remove(product) 처럼 전달합니다.&lt;/li&gt;
&lt;li&gt;database context의 SaveChanges method를 호출하면 change tracker는 insert, update, delete수행에 필요한 SQL 문을 생성해 실행하고 적용된 entity의 수를 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 7에서는 사전에 load 해야 할 entity와 이들에 대한 변경점을 필요로 하지 않음으로써 더 효휼적으로 update와 delete를 수행할 수 있는 ExecuteDelete와 ExecuteUpdate(Async method도 동일함) 2개의 method를 도입하였습니다. 이들은 LINQ query상에서 호출되어 query 결과에 대한 entity에 영향을 주게 됩니다. query가 entity를 검색하는 데 사용되지 않으므로 어떠한 entity도 data context로 load 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 table의 모든 row를 삭제하고자 한다면 ExecuteDelete 혹은 ExecuteDeleteAsync method를 모든 DbSet속성으로 아래와 같이 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690356649556&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await db.Products.ExecuteDeleteAsync();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 code는 database에서 다음과 같은 SQL문을 실행할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690356712029&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELETE FROM Products&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 50보다 더 높은 UnitPrice를 가진 product를 삭제하고자 한다면 아래와 같이 LINQ를 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690356844835&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await db.Products.Where(product =&amp;gt; product.Cost &amp;gt; 50).ExecuteDeleteAsync();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 code는 database에서 다음과 같은 SQL문을 실행할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690356872940&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELETE FROM Products p WHERE p.Cost &amp;gt; 50&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ExecuteUpdate와 ExecuteDelete는 단지 단일 table상에서만 동작할 수 있습니다. 만약 다수의 table에 걸친 꽤 복잡한 LINQ query를 작성했다고 하더라도 단일 table에서만 update나 delete가 수행됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 예로 Discontinued가 false인 모든 product의 UintPrice값을 10% 인상으로 update 하고자 한다면 LINQ는 아래와 같이 작성될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690357536122&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await db.Products.Where(product =&amp;gt; !product.Discontinued).ExecuteUpdateAsync(s =&amp;gt; s.SetProperty(
p =&amp;gt; p.Cost, //update할 속성 선택
p =&amp;gt; p.Cost * 0.1m)); //속성 update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제와 같은 경우 하나의 LINQ안에서 다수의 속성을 update 하기 위해 같은 query에서 다수의 SetProperty를 호출해 연&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifications.cs에서 ExecuteUpdate를 사용해 name이 지정한 값으로 시작하는 모든 product를 update 하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690358193962&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static (int affected, int[]? productIds) IncreaseProductPricesBetter(string productNameStartsWith, decimal amount)
{
    using (Northwind db = new())
    {
        if (db.Products is null)
            return (0, null);

        IQueryable&amp;lt;Product&amp;gt;? products = db.Products.Where(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

        int affected = products.ExecuteUpdate(s =&amp;gt; s.SetProperty(
        p =&amp;gt; p.Cost,
        p =&amp;gt; p.Cost + amount));

        int[] productIds = products.Select(p =&amp;gt; p.ProductId).ToArray();

        return (affected, productIds);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 IncreaseProductPricesBetter method를 호출하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690358303169&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var resultUpdateBetter = IncreaseProductPricesBetter(productNameStartsWith: &quot;Apple&quot;, amount: 20M);

if (resultUpdateBetter.affected &amp;gt; 0)
{
    Console.WriteLine(&quot;Increase product price successful.&quot;);
}

ListProducts(productIdsToHighlight: resultUpdateBetter.productIds);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 위 예제를 실행하기 전 Apple로 시작하는 product를 이전에 삭제하였으므로 위에서 구현했던 새로운 product를 추가하는 문을 먼저 실행하도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 현재 'Apple'로 시작하는 product에 대해 다음과 같이 cost를 변경하였음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOwgYU/btsFjI0DVy6/7HRnvrdv00qkrK0XGNh4b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOwgYU/btsFjI0DVy6/7HRnvrdv00qkrK0XGNh4b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOwgYU/btsFjI0DVy6/7HRnvrdv00qkrK0XGNh4b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOwgYU%2FbtsFjI0DVy6%2F7HRnvrdv00qkrK0XGNh4b0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Modifications.cs로 돌아와 ExecuteDelete를 사용해 지정한 값으로 시작되는 모든 product를 삭제하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690358625569&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int DeleteProductsBetter(string productNameStartsWith)
{
    using (Northwind db = new())
    {
        int affected = 0;
        IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

        if ((products is null) || (!products.Any()))
        {
            Console.WriteLine(&quot;No products found to delete.&quot;);
            return 0;
        }
        else
        {
            affected = products.ExecuteDelete();
        }

        return affected;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 DeleteProductsBetter method를 호출하도록 하고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690358750148&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;About to delete all products whose name starts with Apple.&quot;);
Console.Write(&quot;Press Enter to continue or any other key to exit: &quot;);

if (Console.ReadKey(intercept: true).Key == ConsoleKey.Enter)
{
    int deleted = DeleteProductsBetter(productNameStartsWith: &quot;Apple&quot;);
    Console.WriteLine($&quot;{deleted} product(s) were deleted.&quot;);
}
else
{
    Console.WriteLine(&quot;Delete was canceled.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VU21B/btsFocMWWKf/qQFjibMQlxDpRxLcs86CoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VU21B/btsFocMWWKf/qQFjibMQlxDpRxLcs86CoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VU21B/btsFocMWWKf/qQFjibMQlxDpRxLcs86CoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVU21B%2FbtsFocMWWKf%2FqQFjibMQlxDpRxLcs86CoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;403&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 기존 방식인 변경 추적과 ExecuteUpdate, ExecuteDelete method를 섞어서 사용하게 되는 경우라면 이들은 서로 동기화되지 않음에 주의해야 합니다. Change tracker는 이들 method를 통해 update 하고 delete 한 사항을 알지 못합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Database context pooling&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext class는 일회용이며 단일 작업 단위 원칙에 따라 설계되었습니다. 이전 예제에서는 DbContext에서 파생된 Northwind instance를 using안에서 생성했으므로 Dispose속성은 각 작업에서의 종료시점에 호출됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core와 관련된 ASP.NET Core의 특징 중 하나는 website와 service를 구축할 때 database context를 pooling 함으로써 code를 더욱 효휼적으로 만든다는 것입니다. 이때 따라 가능한한 더 효휼적으로 원하는 만큼의 DbContext에서 파생된 다수의 개체를 생성하고 소멸할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;7. Transaction&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SaveChanges method를 호출할 때마다 암시적으로 transaction이 시작되므로 무엇인가 잘못된 상황이 발생하면 자동적으로 변경사항이 rollback 됩니다. Transaction이내에서 다수의 변경사항이 성공적으로 수행된다면 transaction은 모든 변경사항을 commit 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction은 일련의 변경사항들을 처리하는 동안 해당 data에 대한 읽기/쓰기를 방지하지 위해 lock을 적용함으로써 database에 대한 무결성을 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction은 아래 설명에 따른 약자로 ACID라고 말하기도 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A는 원자성(atomic)입니다. Transaction에 대한 모든 동작이 commit 되거나 commit 되지 않습니다.&lt;/li&gt;
&lt;li&gt;C는 일관성(consistent)입니다. Transaction전후에 대한 database의 상태는 일관되며 이는 code의 logic에 의존합니다. 예를 들어 은행 계좌 간 돈이 송금될 때 하나의 계정에서 100만 원이 인출되면 다른 계정에서 100만원이 입금되어야 하는데 이를 보장하는 것은 business loginc에 달려있습니다.&lt;/li&gt;
&lt;li&gt;I는 격리성(isolated)입니다. Transaction동안 변경사항은 다른 process로부터는 감춰지는데 여기에 선태가능한 여러 격리 수준이 존재합니다(아래 표 참고). 수준이 높을수록 data의 원자성은 더 나아지지만 더 많은 lock이 적용되고 이는 다른 process에 부정적인 영향을 줄 수 있습니다. Snapshot은 lock을 회피하기 위해 row의 여러 복사본을 생성하므로 좀 더 특별한 경우라고 할 수 있지만 transaction이 발생하는 동안 database의 크기를 증가시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;D는 지속성(durable)입니다. Transaction이 실패하면 원상태로 복구될 수 있습니다. 이는 대게 2단계 commit과 transaction log로 구현됩니다. 일단 transaction이 commit 되면 후속 오류가 발생하더라도 data의 보존을 보증합니다. 지속성의 반대는 휘발성(volatile)입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 격리 수준을 통한 transaction 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction은 격리 수준을 설정함으로써 아래 표의 설명과 같이 제어할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 134px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.6821%; height: 20px;&quot;&gt;격리 수준&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%; height: 20px;&quot;&gt;잠금&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;허용되는 무결성 문제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;width: 18.6821%; height: 37px;&quot;&gt;ReadUncommitted&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%; height: 37px;&quot;&gt;해당없음&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 37px;&quot;&gt;Dirty read, non-repeatable read와 phantom data&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 60px;&quot;&gt;
&lt;td style=&quot;width: 18.6821%; height: 60px;&quot;&gt;ReadCommitted&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%; height: 60px;&quot;&gt;data변경시 data에 접근하는 다른 사용자를 transaction이 종료될때까지 차단하기 위해 읽기 잠금을 적용합니다.&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 60px;&quot;&gt;Non-repeatable read와 phantom data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.6821%; height: 17px;&quot;&gt;RepeatableRead&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%; height: 17px;&quot;&gt;data를 읽을때 data에 접근하는 다른 사용자를 transaction이 종료될때까지 차단하기 위해 변경 잠금을 적용합니다.&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Phantom data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6821%;&quot;&gt;Serializable&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%;&quot;&gt;Insert나 delete를 포함해 결과에 영향을 줄 수 있는 모든 동작을 차단하기 위해 key-range잠금을 적용합니다.&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;해당없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6821%;&quot;&gt;Snapshot&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%;&quot;&gt;해당없음&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;해당없음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 명시적인 transaction 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database context에는 Database 속성이 있으며 이를 통해 transaction을 명시적으로 실행시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifications.cs에서 아래와 같이 namespace를 import 합니다. 이는 IDbContextTransaction interface를 사용하기 위한 EF Core storage namespace입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690423530539&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore.Storage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DeleteProducts method에서 db 변수의 instance를 생성한 직후 transaction을 명시적으로 시작하고 격리 수준을 출력하는 문을 추가합니다. Method의 끝에서는 transaction을 commit 하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690425484794&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int DeleteProducts(string productNameStartsWith)
{
    using (Northwind db = new())
    {
        using (IDbContextTransaction t = db.Database.BeginTransaction())
        {
            Console.WriteLine(&quot;Transaction isolation level: {0}&quot;, arg0: t.GetDbTransaction().IsolationLevel);

            IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

            if ((products is null) || (!products.Any()))
            {
                Console.WriteLine(&quot;No products found to delete.&quot;);

                return 0;
            }
            else
            {
                if (db.Products is null) return 0;
                db.Products.RemoveRange(products);
            }

            int affected = db.SaveChanges();

            t.Commit();

            return affected;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc9oAs/btsFlS9wPMf/F7kn6075OkAKKiUC7tF1sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc9oAs/btsFlS9wPMf/F7kn6075OkAKKiUC7tF1sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc9oAs/btsFlS9wPMf/F7kn6075OkAKKiUC7tF1sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc9oAs%2FbtsFlS9wPMf%2FF7kn6075OkAKKiUC7tF1sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;212&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>c#</category>
      <category>ef core</category>
      <category>TRACKING</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/600</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-10-Entity-Framework-Core#entry600comment</comments>
      <pubDate>Thu, 7 Mar 2024 16:46:08 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 9. File, Streams, Serialization</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-9-File-Streams-Serialization</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Data에 대한 입출력의 대상은 file이나 stream이 될 수 있으며 때로는 text를 encoding 하거나 직렬화할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. File System 관리&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application에서는 종종 다른 환경에서 file이나 directory등으로 입출력 동작을 수행해야 할 경우가 있으며 System 및 System.IO namespace에서는 이러한 목적의 class들을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) cross-platform 환경및 filesystem&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 cross-platform환경을 처리하는 방법과 Windows와 Linux 또는 macOS사이의 차이점에 대해 알아보고자 합니다. Windows와 macOS 그리고&amp;nbsp; Linux에서 경로는 다르게 취급되고 있으므로 .NET이 이를 어떻게 처리하는지를 알아둘 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy08 Solution에서 WorkingWithFileSystems이름의 Console App project를 생성합니다.&amp;nbsp; 그리고 project에 Helpers.cs 이름의 class file을&amp;nbsp; 추가한뒤 아래와 같이 SectionTitle method를 가진 partial Program class를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688795935958&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void SectionTitle(string title)
{
	ConsoleColor previousColor = System.Console.ForegroundColor;
	System.Console.ForegroundColor = ConsoleColor.Yellow;
	Console.WriteLine(&quot;*&quot;);
	Console.WriteLine($&quot;* {title}&quot;);
	Console.WriteLine(&quot;*&quot;);
	System.Console.ForegroundColor = previousColor;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 System.IO.Directory, System.Environment, System.IO.Path namespace를 정적 import한뒤&lt;/p&gt;
&lt;pre id=&quot;code_1688796036276&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.IO.Directory; 
using static System.IO.Path; 
using static System.Environment;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 경로 및 directory분리문자를 출력하고, 현재 directory에 대한 경로와 system file, 임시 file, document 등 일부 특별한 용도의 directory에 대한 경로를 출력하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688796417325&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Path.PathSeparator&quot;, arg1: PathSeparator);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Path.DirectorySeparatorChar&quot;, arg1: DirectorySeparatorChar);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Directory.GetCurrentDirectory()&quot;, arg1: GetCurrentDirectory());
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Environment.CurrentDirectory&quot;, arg1: CurrentDirectory);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Environment.SystemDirectory&quot;, arg1: SystemDirectory);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Path.GetTempPath()&quot;, arg1: GetTempPath());

Console.WriteLine(&quot;GetFolderPath(SpecialFolder)&quot;);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot; .System)&quot;, arg1: GetFolderPath(SpecialFolder.System));
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot; .ApplicationData)&quot;, arg1: GetFolderPath(SpecialFolder.ApplicationData));
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot; .MyDocuments)&quot;, arg1: GetFolderPath(SpecialFolder.MyDocuments));
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot; .Personal)&quot;, arg1: GetFolderPath(SpecialFolder.Personal));&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Environment type에서는 위의 예제에서 사용된 것 이외 GetEnvironmentVariables method와 OSVersion, ProcessorCount속성등 여러 가지 유용한 member들을 가지고 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv574P/btsFhfWLBES/JW8FjSPBXSVro3YJzlk5uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv574P/btsFhfWLBES/JW8FjSPBXSVro3YJzlk5uK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv574P/btsFhfWLBES/JW8FjSPBXSVro3YJzlk5uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv574P%2FbtsFhfWLBES%2FJW8FjSPBXSVro3YJzlk5uK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;286&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio Code에서 dotnet run을 사용해 console app을 실행하는 경우 CurrentDirectory는 bin folder 내부가 아닌&amp;nbsp; project folder가 될 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Windows에서는 directory에 대한 구분문자로 backslash(\)문자를 사용하지만 macOS와 Linux는 slash문자를 사용합니다. 경로를 결합할 때 code에서 어떤 문자가 사용되는지를 미리 가정하지 말고 Path.DirectorySeparatorChar를 사용하는 것이 좋습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Drive&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Drive에 관한 것으로는 computer에 연결된 모든 drive에 관한 정보를 반환하는 정적 method를 가진 DriveInfo type을 사용합니다. 이때 각각의 drive는 drive type을 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 모든 drive를 가져와 사용가능한 것들에 대해서만 drive의 이름, type, size, 사용 가능한 용량 그리고 format을 출력하는 문을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688803703765&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;My drives&quot;);
Console.WriteLine(&quot;{0,-30} | {1,-10} | {2,-7} | {3,18} | {4,18}&quot;, &quot;NAME&quot;, &quot;TYPE&quot;, &quot;FORMAT&quot;, &quot;SIZE (BYTES)&quot;, &quot;FREE SPACE&quot;);
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
	if (drive.IsReady)
	{
		Console.WriteLine(&quot;{0,-30} | {1,-10} | {2,-7} | {3,18:N0} | {4,18:N0}&quot;, drive.Name, drive.DriveType, drive.DriveFormat, drive.TotalSize, drive.AvailableFreeSpace);
	}
	else
	{
		Console.WriteLine(&quot;{0,-30} | {1,-10}&quot;, drive.Name, drive.DriveType);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;TotalSize와 같은 속성을 읽기 전에 drive가 준비상태인지를 확인해야 합니다. 그렇지 않으면 이동식 drive에 관한 예외를 보게 될 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qA4HX/btsFcQdyQre/ihN1kfWxMuH4Ub05ZWLnk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qA4HX/btsFcQdyQre/ihN1kfWxMuH4Ub05ZWLnk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qA4HX/btsFcQdyQre/ihN1kfWxMuH4Ub05ZWLnk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqA4HX%2FbtsFcQdyQre%2FihN1kfWxMuH4Ub05ZWLnk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;172&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Linux에서 일반 사용자 권한으로 예제를 실행하는 경우 Name과 DriveType만 접근이 가능하고 DriveFormat과 TotalSize에서는 UnauthorizedAccessException예외를 일으킬 수 있습니다.이 문제를 해결하기 위해서는 ' sudo dotnet run.'명령을 통해서 관리자 권한으로 예제를 실행해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Directory&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Directory와 관련해서는 Directory, Path 그리고 Environment 적정 class를 사용합니다. 이들 type에는 filesystem에 관한 다수의 member들을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자지정 경로를 구성할 때는 예를 들어 directory분리문자를 사용할 때 platform에 대한 가정을 하지 않도록 code작성에 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 directory 이름에 대한 문자열을 생성하고 Path type의 Combine method를 통해 적절히 이들을 결합하여 사용자의 home directory 아래에 사용자지정 경로를 정의하도록 하는 문을 아래와 같이 작성합니다. 이때 사용자지정 directory에 대한 경로는 Directory class의 Exists method를 통해 존재여부를 확인하고 directory를 생성한 뒤 Directory class의 CreateDirectory method와 Delete method를 사용해 이곳에 포함된 모든 file과 하위 directory를 포함해 삭제하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688805326932&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;Custom Directory&quot;);
			
string newFolder = Combine(GetFolderPath(SpecialFolder.Personal), &quot;NewFolder&quot;);

Console.WriteLine($&quot;Working with: {newFolder}&quot;);
Console.WriteLine($&quot;Does it exist? {Path.Exists(newFolder)}&quot;);
Console.WriteLine(&quot;Creating it...&quot;);
			
CreateDirectory(newFolder);

Console.WriteLine($&quot;Does it exist? {Path.Exists(newFolder)}&quot;);
Console.Write(&quot;Confirm the directory exists, and then press ENTER: &quot;); Console.ReadLine();
Console.WriteLine(&quot;Deleting it...&quot;);
			
Delete(newFolder, recursive: true);

Console.WriteLine($&quot;Does it exist? {Path.Exists(newFolder)}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 뒤 결과를 확인합니다. directory를 삭제하기 위해 Enter key를 누르기 전 해당 directory가 정상적으로 만들어졌는지 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJmlm/btsFethOBhg/s9Rnm4dSLyEOnKDO3JFpV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJmlm/btsFethOBhg/s9Rnm4dSLyEOnKDO3JFpV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJmlm/btsFethOBhg/s9Rnm4dSLyEOnKDO3JFpV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJmlm%2FbtsFethOBhg%2Fs9Rnm4dSLyEOnKDO3JFpV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;267&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET 6 이전까지는 Directory class에서만 Exists method가 있었으나 .NET 7 이후부터는 Path에도 Exists method가 존재하며 둘의 역활은 동일합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) File&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File과 관련해서는 Directory에서 했던 것처럼 file type을 정적으로 가져올 수 있습니다. 다만 아래 예제에서는 directory type에서와 이름이 같은 method를 일부 가지고 있으며 따라서 서로 충돌할 수 있기 때문에 그런 방식을 사용하지 않았습니다. 또한 이 경우 큰 문제가 되지 않을 만큼 짧은 이름을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 파일존재를 확인한 뒤 text file을 생성하고 해당 file에 text를 기록하는 문을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688863268472&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;files&quot;);

string dir = Combine(GetFolderPath(SpecialFolder.Personal), &quot;OutputFiles&quot;);
CreateDirectory(dir);

string textFile = Combine(dir, &quot;Dummy.txt&quot;);
string backupFile = Combine(dir, &quot;Dummy.bak&quot;);

Console.WriteLine($&quot;Working with: {textFile}&quot;);
Console.WriteLine($&quot;Does it exist? {File.Exists(textFile)}&quot;);

StreamWriter textWriter = File.CreateText(textFile);
textWriter.WriteLine(&quot;Hello, C#!&quot;);
textWriter.Close();

Console.WriteLine($&quot;Does it exist? {File.Exists(textFile)}&quot;);

File.Copy(sourceFileName: textFile, destFileName: backupFile, overwrite: true);
Console.WriteLine($&quot;Does {backupFile} exist? {File.Exists(backupFile)}&quot;);

Console.Write(&quot;Confirm the files exist, and then press ENTER: &quot;);
Console.ReadLine();
			
File.Delete(textFile);
Console.WriteLine($&quot;Does it exist? {File.Exists(textFile)}&quot;);
			
Console.WriteLine($&quot;Reading contents of {backupFile}:&quot;);
StreamReader textReader = File.OpenText(backupFile);
Console.WriteLine(textReader.ReadToEnd());
textReader.Close();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 file을 기록하고 나면 system resource와 잠금을 해제하기 위해 file을 닫아줍니다.(이것은 일반적으로 file작성 시 예외가 발생하더라도 file이 닫히는 경우를 확실히 하기 위해 try-finally문에서 이루어집니다.) 그리고 backup을 위해 file을 복사한 뒤 기존의 file은 삭제하고 backup file로부터 내용을 읽어 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH2Lff/btsFcEdgkM6/88UykhSp0xVC9RJ0aKycAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH2Lff/btsFcEdgkM6/88UykhSp0xVC9RJ0aKycAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH2Lff/btsFcEdgkM6/88UykhSp0xVC9RJ0aKycAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH2Lff%2FbtsFcEdgkM6%2F88UykhSp0xVC9RJ0aKycAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;267&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Path&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 경로의 일부만을 확인해야 하는 경우도 있을 것입니다. 예를 들어 경로에서 folder이름이나 file이름, 혹은 확장자와 같은 것만을 추출해야 하는 경우입니다. 또는 임시 folder나 file이름을 생성해야 하는 경우도 있습니다. 이와 같은 작업은 Path class의 정적 method를 사용하면 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 아래와 같은 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688868332364&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;Paths&quot;);
string dir = Combine(GetFolderPath(SpecialFolder.Personal), &quot;OutputFiles&quot;);
string textFile = Combine(dir, &quot;Dummy.txt&quot;);

Console.WriteLine($&quot;Folder Name: {GetDirectoryName(textFile)}&quot;);
Console.WriteLine($&quot;File Name: {GetFileName(textFile)}&quot;);
Console.WriteLine(&quot;File Name without Extension: {0}&quot;,

GetFileNameWithoutExtension(textFile));

Console.WriteLine($&quot;File Extension: {GetExtension(textFile)}&quot;);
Console.WriteLine($&quot;Random File Name: {GetRandomFileName()}&quot;);
Console.WriteLine($&quot;Temporary File Name: {GetTempFileName()}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Wb4S/btsFfWKrfEc/DuhQAsAGk3b0dHrKDnCml0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Wb4S/btsFfWKrfEc/DuhQAsAGk3b0dHrKDnCml0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Wb4S/btsFfWKrfEc/DuhQAsAGk3b0dHrKDnCml0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Wb4S%2FbtsFfWKrfEc%2FDuhQAsAGk3b0dHrKDnCml0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;229&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GetTempFileName은 zero-byte file을 생성하여 사용할 수 있도록 준비한 뒤 해당 file의 이름을 반환합니다. 하지만 GetRandomFileName은 file의 이름만 반환할 뿐 실제 생성하지는 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) File 정보 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File이나 directory에 관한 정보, 예를 들어 size나 마지막 접근일자와 같은 정보를 를 확인하려면 FileInfo 혹은 DirectoryInfo class의 instance를 생성해 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FileInfo와 DirectoryInfo는 모두 FileSystemInfo class로부터 상속받은 것으로 따라서 둘 다 자신만의 속성과 더불어&amp;nbsp; LastAccessTime나 Delete와 같은 공통된 member를 가지고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 49px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.0465%; height: 17px;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 83.9535%; height: 17px;&quot;&gt;Members&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 15px;&quot;&gt;
&lt;td style=&quot;width: 16.0465%; height: 15px;&quot;&gt;FileSystemInfo&lt;/td&gt;
&lt;td style=&quot;width: 83.9535%; height: 15px;&quot;&gt;Field : FullPath, OriginalPath&lt;br /&gt;Property : Attributes, CreationTime, CreationTimeUtc, Exists, Extension, FullName, LastAccessTime, LastAccessTimeUtc, LastWriteTime, LastWriteTimeUtc, Name Methods: Delete, GetObjectData, Refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.0465%; height: 17px;&quot;&gt;DirectoryInfo&lt;/td&gt;
&lt;td style=&quot;width: 83.9535%; height: 17px;&quot;&gt;Property : Parent, Root&lt;br /&gt;Method : Create, CreateSubdirectory, EnumerateDirectories, EnumerateFiles, EnumerateFileSystemInfos, GetAccessControl, GetDirectories, GetFiles, GetFileSystemInfos, MoveTo, SetAccessControl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.0465%;&quot;&gt;FileInfo&lt;/td&gt;
&lt;td style=&quot;width: 83.9535%;&quot;&gt;Property : Directory, DirectoryName, IsReadOnly, Length&lt;br /&gt;Methods: AppendText, CopyTo, Create, CreateText, Decrypt, Encrypt, GetAccessControl, MoveTo, Open, OpenRead, OpenText, OpenWrite, Replace, SetAccessControl&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 하나의 File에 대해 FileInfo instance를 통해 여러 동작을 어떻게 효휼적으로 수행할 수 있는지를 알려주기 위한 것으로 Program.cs에서 backup file에 대한 FileInfo instance를 생성하고 console에 이에 대한 정보를 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688879220506&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;File information&quot;);

string dir = Combine(GetFolderPath(SpecialFolder.Personal), &quot;OutputFiles&quot;);
string backupFile = Combine(dir, &quot;Dummy.bak&quot;);

FileInfo info = new(backupFile);
Console.WriteLine($&quot;{backupFile}:&quot;);
Console.WriteLine($&quot;Contains {info.Length} bytes&quot;);
Console.WriteLine($&quot;Last accessed {info.LastAccessTime}&quot;);
Console.WriteLine($&quot;Has readonly set to {info.IsReadOnly}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTyhcm/btsFdVllP0E/XQ8ZqayhxHkbKzhK6oqgf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTyhcm/btsFdVllP0E/XQ8ZqayhxHkbKzhK6oqgf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTyhcm/btsFdVllP0E/XQ8ZqayhxHkbKzhK6oqgf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTyhcm%2FbtsFdVllP0E%2FXQ8ZqayhxHkbKzhK6oqgf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;210&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;byte에 대한 숫자는 OS마다 다른 line ending을 사용할 수 있으므로 다르게 표시될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) File에 대한 처리방식 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File에 대한 어떤 처리를 진행하고자 할 때 경우에 따라 어떤 형태로 file을 open 해야 할지 지정하는 경우가 있으며 이런 경우 File.Open method를 사용할 수 있습니다. Open method는 enum값을 통해 추가적인 option을 지정할 수 있는 overload를 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용가능한 enum값으로는 다음과 같은 것이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FileMode : CreateNew, OpenOrCreate, Truncate 등 해당 file에 무엇을 할지 지정합니다.&lt;/li&gt;
&lt;li&gt;FileAccess : ReadWrite와 같이 필요한 접근 수준을 지정합니다.&lt;/li&gt;
&lt;li&gt;FileShare : 다른 process에서 Read와 같이 접근가능한 수준을 지정함으로써 file에 대한 잠금을 조정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 만약 file을 열고 해당 file로부터 읽기를 수행하면서 다른 process에서도 역시 읽기를 허용하려면 다음과 같이 지정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688952949476&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FileStream file = File.Open(textFile, FileMode.Open, FileAccess.Read, FileShare.Read);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 file에 대한 속성을 위해 사용가능한 아래와 enum값도 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FileAttributes : FileSystemInfo에서 파생된 type의 attrubute 속성에서 Archive와 Encrypted와 같은 값을 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 file이나 directory의 속성은 아래와 같이 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688953328017&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FileInfo info = new(backupFile);
WriteLine(&quot;Is the backup file compressed? {0}&quot;, info.Attributes.HasFlag(FileAttributes.Compressed));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. Stream 읽기/쓰기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream은 읽고 쓸 수 있는 일련의 byte입니다. File을 array처럼 처리할 수 있지만 file내 byte의 위치를 알면 random access가 제공되므로 순차적으로 접근할 수 있는 stream으로서 file을 처리하는 것이 유용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream은 또한 terminal 입출력 및 random access를 제공하지 않으며 위치를 탐색(즉, 이동)할 수 없는 socket과 port 같은 networking resource를 처리하는 데에도 사용할 수 있습니다. 이에 따라 data stream이 어디서 온 것인지 신경 쓸 필요 없이 임의의 byte를 처리할 수 있는 code를 작성할 수 있으며 이때 code는 단순히 stream을 읽고 쓰기만 하면 다른 한편에서는 실제 byte가 저장된 곳을 처리할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Stream의 추상화 및 구체화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream이라는 추상화 class는 stream에 대한 모든 type을 표현합니다. 추상화 class자체는 new를 사용해 instance를 생성할 수 없으며 상속만이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 해당 기반 class로부터 상속된 다수의 구체화 class가 존재하는데 여기에는 FileStream, MemoryStream, BufferedStream, GZipStream 그리고 SslStream과 같은 것이 존재하며 이들은 모두 같은 방식으로 작동합니다. 모든 stream은 IDisposable interface를 구현하고 있으므로 비관리 resource를 해제하기 위한 Dispose method를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream class에서 가지고 있는 일부 공통 member에 관해서는 아래 표에서 확인할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 174px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 17px;&quot;&gt;Member&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 40px;&quot;&gt;CanRead, CanWrite&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 40px;&quot;&gt;해당 속성은 stream을 읽거나 쓸수 있는지의 여부를 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 40px;&quot;&gt;Length, Position&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 40px;&quot;&gt;해당 속성은 총 byte길이와 stream에서의 현재 위치를 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 20px;&quot;&gt;Dispose&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 20px;&quot;&gt;해당 method는 stream을 닫고 resource를 해제합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 40px;&quot;&gt;Flush&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 40px;&quot;&gt;Stream이 bufer를 가지고 있다면 해당 method는 buffer의 byte를 stream에 기록하고 buffer를 비웁니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 17px;&quot;&gt;CanSeek&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 17px;&quot;&gt;해당 속성은 Seek method가 사용가능한지를 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;Seek&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 현재 위치를 매개변수로 지정된 곳으로 이동합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;Read, ReadAsync&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 stream으로 부터 지정한 수의 byte를 byte배열로 읽고 위치를 앞당깁니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;ReadByte&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 stream으로 부터 다음 byte를 읽고 위치를 앞당깁니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;Write, WriteAsync&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 byte array의 내용을 stream으로 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;WriteByte&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 byte를 stream으로 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Storage stream&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표에서는 byte가 저장될 위치를 나타내는 일부 storage stream을 설명하고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.217%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 31.2403%;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 45.5426%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.217%;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 31.2403%;&quot;&gt;FileStream&lt;/td&gt;
&lt;td style=&quot;width: 45.5426%;&quot;&gt;filesystem에 저장된 byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.217%;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 31.2403%;&quot;&gt;MemoryStream&lt;/td&gt;
&lt;td style=&quot;width: 45.5426%;&quot;&gt;현재 process의 memory에 저장된 byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.217%;&quot;&gt;System.Net.Sockets&lt;/td&gt;
&lt;td style=&quot;width: 31.2403%;&quot;&gt;NetworkStream&lt;/td&gt;
&lt;td style=&quot;width: 45.5426%;&quot;&gt;network 위치에 저장된 byte&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;File Stream은&lt;span&gt; Windows상에서&lt;/span&gt;&lt;/span&gt; 성능과 안정성이 더육 향상되도록 .NET 6에서 재작성되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/file-io-improvements-in-dotnet-6/&quot;&gt;File IO improvements in .NET 6 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Function stream&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 자체적으로는 존재하지 않지만 다른 stream에 연결되어 기능을 추가시키는 function stream을 설명하고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Security.Cryptography&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;CryptoStream&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Stream을 암호화/복호화 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.IO.Compression&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;GZipStream, DeflateStream&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Stream을 압축하고 해제합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Net.Security&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;AuthenticatedStream&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Stream전역에 자격증명을 보냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Stream helper&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 저수준에서 stream을 사용해야 하는 경우가 있겠지만 대부분은 helper class를 사용하는 것만으로 손쉽게 구현할 수 있습니다. Stream에 대한 모든 helper type은 IDisposable을 구현하고 있으므로 비관리 resource를 해제하기 위한 Dispose method를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 상황에서 사용가능한 몇몇 helper class는 아래 표에 안내되어 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 205px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 17px;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 17px;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 40px;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 40px;&quot;&gt;StreamReader&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 40px;&quot;&gt;기본 stream으로 부터 일반 text를 읽습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 40px;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 40px;&quot;&gt;StreamWriter&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 40px;&quot;&gt;기본 stream으로 일반 text를 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 57px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 57px;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 57px;&quot;&gt;BinaryReader&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 57px;&quot;&gt;Stream으로 부터 .NET type을 읽습니다. 예를 들어 ReadDecimal method는 기본 stream에서 decimal 값을 다음 16 byte를 읽고 ReadInt32 method는 int값으로 다음 4 byte를 읽습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 17px;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 17px;&quot;&gt;BinaryWriter&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 17px;&quot;&gt;Stream으로 .NET type을 작성합니다. 예를 들어 decimal 매개변수를 가진 Write method는 기본 stream으로 16byte를 작성하며 int 매개변수를 가진 Write method는 4 byte를 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 17px;&quot;&gt;System.Xml&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 17px;&quot;&gt;XmlReader&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 17px;&quot;&gt;기본 stream으로 부터 XML format을 사용해 읽습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 17px;&quot;&gt;System.Xml&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 17px;&quot;&gt;XmlWrite&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 17px;&quot;&gt;기본 stream으로 XML format을 사용해 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Stream pipeline 구축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StreamWriter와 같은 helpler와 CryptoStream이나 GZipStream과 같은 여러 기능 Stream을 stream을 저장하는 FileStream과 pipeline으로 결합하는 것은 흔한 경우입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqoGCD/btsFf4pt9JZ/O2JJm67OV9VVzok1OvzKVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqoGCD/btsFf4pt9JZ/O2JJm67OV9VVzok1OvzKVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqoGCD/btsFf4pt9JZ/O2JJm67OV9VVzok1OvzKVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqoGCD%2FbtsFf4pt9JZ%2FO2JJm67OV9VVzok1OvzKVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;904&quot; height=&quot;95&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WriteLine method를 통해 hello라는 문자열을 pipeline을 통해서 보내면 이것은 암호화와 압축 stream을 거쳐게 되고 최종목적지에 도달하게 되면 'G7x'라고 쓰여진 file의 결과물을 얻게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Text stream&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream으로 text를 작성하는 예제를 만들기 위해 csStudy09 solution을 생성하고 이어서 WorkingWithStreams이름의 Console app project를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project가 생성되면 Viper.cs라는 이름의 class file을 추가하고 Callsigns이름의 문자열배열값을 가진 Viper이름의 정적 class를 아래와 같이 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 System.Xml namespace와 System.Environment, System.IO.path namespace를 정적 import 합니다. 그리고 Viper call signs를 열거하면서 각각 한 줄씩 text file에 기록하도록 하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688974601496&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string textFile = Combine(CurrentDirectory, &quot;streams.txt&quot;);
            
// text file을 생성한 뒤 helper writer를 반환
StreamWriter text = File.CreateText(textFile);
            

foreach (string item in Viper.Callsigns)
{
    text.WriteLine(item);
}

text.Close(); // release resources

// file의 크기와 내용 출력
Console.WriteLine(&quot;{0} contains {1:N0} bytes.&quot;, arg0: GetFileName(textFile), arg1: new FileInfo(textFile).Length);
Console.WriteLine(File.ReadAllText(textFile));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caN0Wz/btsFcklFklN/Mu3nvUzN6evF4Qx7Nbm7qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caN0Wz/btsFcklFklN/Mu3nvUzN6evF4Qx7Nbm7qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caN0Wz/btsFcklFklN/Mu3nvUzN6evF4Qx7Nbm7qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaN0Wz%2FbtsFcklFklN%2FMu3nvUzN6evF4Qx7Nbm7qk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;207&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) XML stream&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML 요소를 작성하는 데는 2가지 방식이 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WriteStartElement와 WriteEndElement : 요소가 자식요소를 가지는 경우 같이 사용합니다.&lt;/li&gt;
&lt;li&gt;WriteElementString : 요소가 자식요소를 가지지 않는 경우 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽거나 쓰기 위해 file을 열면 .NET의 외부 resource를 사용하는 것으로 이를 비관리 resouce라고 하며 대상 file에 대한 작업이 종료되면 instance는 반드시 소멸되어야 하며&amp;nbsp;대게는 resource를 소멸할 때 try ~ finally block안에서 Dispose method를 호출합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 Viper pilot 호출 sign에 대한 문자열 배열값을 XML file로 저장하도록 하는 것으로 Program.cs에서 호출 sign을 열거하고 각각을 단일 XML file의 요소로 작성하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689033991291&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//작성을 위한 file경로 정의
string xmlFile = Combine(CurrentDirectory, &quot;streams.xml&quot;);

// filestream과 XML writer에 대한 변수 선언
FileStream? xmlFileStream = null;
XmlWriter? xml = null;

try
{
    // file stream 생성
    xmlFileStream = File.Create(xmlFile);
    // file stream을 XML writer helper로 wrapping하고 자동적으로 중첩된 요소간 들여쓰기를 적용합니다.
    xml = XmlWriter.Create(xmlFileStream, new XmlWriterSettings { Indent = true });

    xml.WriteStartDocument();
    // root 요소 작성
    xml.WriteStartElement(&quot;callsigns&quot;);

    //string을 열거하고 각각을 stream에 작성합니다.
    foreach (string item in Viper.Callsigns)
    {
        xml.WriteElementString(&quot;callsign&quot;, item);
    }

    // 종료 root 요소 작성
    xml.WriteEndElement();
    // helper와 stream닫기
    xml.Close();
    xmlFileStream.Close();
}
catch (Exception ex) {
    //해당 경로가 존재하지 않으면 예외발생
    Console.WriteLine($&quot;{ex.GetType()} says {ex.Message}&quot;);
}
finally {
    if (xml != null)
    {
        xml.Dispose();
        Console.WriteLine(&quot;비관리 resource 해제&quot;);
    }

    if (xmlFileStream != null)
    {
        xmlFileStream.Dispose();
        Console.WriteLine(&quot;File stream의 비관리 resource 해제&quot;);
    }
}

//File 내용 출력
Console.WriteLine(&quot;{0} contains {1:N0} bytes.&quot;, arg0: Path.GetFileName(xmlFile), arg1: new FileInfo(xmlFile).Length);
Console.WriteLine(File.ReadAllText(xmlFile));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsyaLu/btsFdqFSxZK/zko2ppqqJERRllYjDr9OKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsyaLu/btsFdqFSxZK/zko2ppqqJERRllYjDr9OKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsyaLu/btsFdqFSxZK/zko2ppqqJERRllYjDr9OKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsyaLu%2FbtsFdqFSxZK%2Fzko2ppqqJERRllYjDr9OKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;282&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Dispose method를 호출하기 전 개체가 null인지를 확인해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) using문을 사용해 소멸을 단순화하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 null 개체에 대한 확인 code를 좀 더 단순화한 다음 using 문을 통해 Dispose method를 호출하도록 바꿀 수 있습니다. 일반적으로는 더 높은 수준에서의 제어가 필요하지 않은 이상에는 수동적으로 Dispose method를 호출하기보다는 using문의 사용을 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;using keyword자체가 namespace를 import 하고 IDisposable를 구현하는 개체에 Dispose를 호출할 finally문을 생성하는 2가지로 사용된다는 측면에서 약간 혼란스러울 수 있지만 실제 사용해 보면 이 둘은 아주 명확히 구분된다는 것을 알 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서 compiler는 using 문 block을 catch문이 없는 try-finally문으로 변경합니다. 물론 여기에서도 try문을 충첩적으로 사용할 수 있으므로 어떤 예외사항을 잡아내야 한다면 아래와 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689036298231&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using (FileStream file = File.OpenWrite(Path.Combine(CurrentDirectory, &quot;test.txt&quot;)))
{
    using (StreamWriter writer = new StreamWriter(file))
    {
        try
        {
            writer.WriteLine(&quot;.NET!&quot;);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;{ex.GetType()} says {ex.Message}&quot;);
        }
    } // 개체가 null이 아니면 자동적으로 Dispose를 호출
} // 개체가 null이 아니면 자동적으로 Dispose를 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 위 예제는 using문을 위한 괄호와 들여 쓰기를 명시하지 않음으로써 아래와 같이 더욱 간소화할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689037248934&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using FileStream file = File.OpenWrite(Path.Combine(CurrentDirectory, &quot;test.txt&quot;));
using StreamWriter writer = new(file);

try
{
    writer.WriteLine(&quot;.NET!&quot;);
}
catch (Exception ex)
{
    Console.WriteLine($&quot;{ex.GetType()} says {ex.Message}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Stream 압축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML은 상대적으로 장황한 편이라 일반적인 text보다 더 많은 byte공간을 차지하게 됩니다. 이제 GZIP이라는 일반적인 압축 algorithm을 사용하여 XML을 얼마나 압축할 수 있는지를 확인해 볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Core 2.1에서 Microsoft는 Brotli이라는 압축 algorithm의 구현을 도입하였습니다. 성능면에서 Brotli는 DEFLATE와 GZIP에서 사용된 algorithm과 비슷하지만 20% 정도 더 압축력이 높다고 평가되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 GZIP과 Brotli 2개의 압축 algorithm을 비교해 보고자 Project에 Compress.cs라는 class file을 추가합니다. Compress.cs에서는 GZipStream과 BrotliStream에 대한 instance를 사용하여 위와 동일한 XML 요소를 포함하는 압축 file을 생성한 다음 읽는 동안 압축을 풀고 console에 해당 내용을 출력하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689063406581&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.IO.Compression; // BrotliStream, GZipStream, CompressionMode
using System.Xml; // XmlWriter, XmlReader
using static System.Environment; // CurrentDirectory
using static System.IO.Path; // Combine

namespace WorkingWithStreams
{
    partial class Program
    {
        static void Compress(string algorithm = &quot;gzip&quot;)
        {
            //file의 확장자를 통해 algorithm을 사용하는 file 경로 정의
            string filePath = Combine(CurrentDirectory, $&quot;streams.{algorithm}&quot;);

            FileStream file = File.Create(filePath);
            Stream compressor;

            if (algorithm == &quot;gzip&quot;)
            {
                compressor = new GZipStream(file, CompressionMode.Compress);
            }
            else
            {
                compressor = new BrotliStream(file, CompressionMode.Compress);
            }

            using (compressor)
            {
                using (XmlWriter xml = XmlWriter.Create(compressor))
                {
                    xml.WriteStartDocument();
                    xml.WriteStartElement(&quot;callsigns&quot;);
                    foreach (string item in Viper.Callsigns)
                    {
                        xml.WriteElementString(&quot;callsign&quot;, item);
                    }
                }
            }

            Console.WriteLine(&quot;{0} contains {1:N0} bytes.&quot;, Path.GetFileName(filePath), new FileInfo(filePath).Length);
            Console.WriteLine($&quot;The compressed contents:&quot;);
            Console.WriteLine(File.ReadAllText(filePath));
            
            Console.WriteLine(&quot;Reading the compressed XML file:&quot;);
            file = File.Open(filePath, FileMode.Open);
            Stream decompressor;

            if (algorithm == &quot;gzip&quot;)
            {
                decompressor = new GZipStream(file, CompressionMode.Decompress);
            }
            else
            {
                decompressor = new BrotliStream(file, CompressionMode.Decompress);
            }

            using (decompressor)
            {

                using (XmlReader reader = XmlReader.Create(decompressor))
                {

                    while (reader.Read())
                    {
                        //callsign 요소가 있는지 확인
                        if ((reader.NodeType == XmlNodeType.Element) &amp;amp;&amp;amp; (reader.Name == &quot;callsign&quot;))
                        {
                            reader.Read(); //요소 내부 text로 이동
                            Console.WriteLine($&quot;{reader.Value}&quot;); //해당 값 읽기
                        }
                    }
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 gzip과 brotli algorithm을 사용하기 위한 매개변수와 함께 Compress method를 호출하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689063497875&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Compress(algorithm: &quot;gzip&quot;);
Compress(algorithm: &quot;brotli&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같이 XML file과 gzip 및 brotli algorithm을 통해 압축된 XML file의 size를 비교하는 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D6thE/btsFfb8R8Rc/f0PpbPRnUM8KlaDEdRAes0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D6thE/btsFfb8R8Rc/f0PpbPRnUM8KlaDEdRAes0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D6thE/btsFfb8R8Rc/f0PpbPRnUM8KlaDEdRAes0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD6thE%2FbtsFfb8R8Rc%2Ff0PpbPRnUM8KlaDEdRAes0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;466&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연한 이야기지만 압축된 file은 압축되지 않은 동일한 file size의 size보다 훨씬 적음을 알 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;압축 mode와 더불어 압축 level역시 선택할 수 있습니다. 자세한 사항은 아래 link를 참고해 주시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot;&gt;CompressionLevel Enum (System.IO.Compression) | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708932155515&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CompressionLevel Enum (System.IO.Compression)&quot; data-og-description=&quot;Specifies values that indicate whether a compression operation emphasizes speed or compression size.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bq1cAu/hyVqq9wJGg/tPSkeUnq3iGGoO9K7jzvok/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bq1cAu/hyVqq9wJGg/tPSkeUnq3iGGoO9K7jzvok/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CompressionLevel Enum (System.IO.Compression)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Specifies values that indicate whether a compression operation emphasizes speed or compression size.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) tar 압축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장자가 .tar인 file은 Unix기반의 압축 application인 tar에 의해 생성된 것입니다. 확장자가 .tag.gz인 file도 존재하는데 이는 tar로 file이 만들어지고 GZIP 압축 algorithm을 사용해 압축되어 생성된 것임을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서는 System.Formats.Tar assembly를 통해 tar 압축 file을 읽고, 생성하며 압축하거나 풀 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 관련해 TarFile class에서는 아래 표에서의 static public member를 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;Member&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;CreateFromDirectory&lt;br /&gt;CreateFromDirectoryAsync&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;지정된 directory로 부터 지정된 모든 filesystem 항목을 포함하는 stream을 생성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;ExtractToDirectory&lt;br /&gt;ExtractToDirectoryAsync&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;지정한 directory에서 tar 압축을 나타내는 stream의 content를 추출합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;DefaultCapacity&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;Windows의 MAX_PATH (260)이 임의 기본 용량으로 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련한 예제 작성을 위해 csStudy09 solution에서 WorkingWithTarArchives이름의 Console app project를 생성합니다. 그리고 Helpers.cs라는 class file을 추가하여 error, 경고, 정보 message를 console에 적절한 색상으로 출력하는 3개의 method를 가진 partial Program class를 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689129665953&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.Console;

namespace WorkingWithTarArchives
{
    partial class Program
    {
        static void WriteError(string message)
        {
            ConsoleColor previousColor = ForegroundColor;
            ForegroundColor = ConsoleColor.Red;
            WriteLine($&quot;FAIL: {message}&quot;);
            ForegroundColor = previousColor;
        }

        static void WriteWarning(string message)
        {
            ConsoleColor previousColor = ForegroundColor;
            ForegroundColor = ConsoleColor.DarkYellow;
            WriteLine($&quot;WARN: {message}&quot;);
            ForegroundColor = previousColor;
        }
        static void WriteInformation(string message)
        {
            ConsoleColor previousColor = ForegroundColor;
            ForegroundColor = ConsoleColor.Blue;
            WriteLine($&quot;INFO: {message}&quot;);
            ForegroundColor = previousColor;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 project의 실행 file이 동작하는 bin folder에 images라는 folder를 생성하고 아래 image들을 내려받은 뒤 해당 folder로 복사해 놓습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/cBWRs2/btsFdXwJ9c2/Z8Fmaf0kw6BeRGyRxoh22k/images.zip?attach=1&amp;amp;knm=tfile.zip&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;images.zip&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;2.48MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 지정한 folder의 content를 tar로 압축한 다음 이것을 다시 새로운 folder로 추출하는 구문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689131271529&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try
{
    string current = Environment.CurrentDirectory;
    WriteInformation($&quot;Current directory: {current.Split(Path.DirectorySeparatorChar).Last()}&quot;);

    string sourceDirectory = Path.Combine(current, &quot;images&quot;);
    string destinationDirectory = Path.Combine(current, &quot;extracted&quot;);
    string tarFile = Path.Combine(current, &quot;images-archive.tar&quot;);

    if (!Directory.Exists(sourceDirectory))
    {
        WriteError($&quot;The {sourceDirectory.Split(Path.DirectorySeparatorChar).Last()} directory must exist. Please create it and add some files to it.&quot;);
        return;
    }

    //tar file이 존재한다면 삭제
    if (File.Exists(tarFile))
    {
        File.Delete(tarFile);
        WriteWarning($&quot;{Path.GetFileName(tarFile)} already existed so it was deleted.&quot;);
    }

    WriteInformation($&quot;Archiving directory: {sourceDirectory.Split(Path.DirectorySeparatorChar).Last()}\n To .tar file: {Path.GetFileName(tarFile)}&quot;);

    TarFile.CreateFromDirectory(sourceDirectoryName: sourceDirectory, destinationFileName: tarFile, includeBaseDirectory: true);

    WriteInformation($&quot;Does {Path.GetFileName(tarFile)} exist? {File.Exists(tarFile)}.&quot;);

    //destinationDirectory folder가 존재하지 않으면 새로 생성
    if (!Directory.Exists(destinationDirectory))
    {
        Directory.CreateDirectory(destinationDirectory);
        WriteWarning($&quot;{destinationDirectory.Split(Path.DirectorySeparatorChar).Last()} did not exist so it was created.&quot;);
    }

    WriteInformation($&quot;Extracting archive: {Path.GetFileName(tarFile)}\n To directory: {destinationDirectory.Split(Path.DirectorySeparatorChar).Last()}&quot;);

    TarFile.ExtractToDirectory(sourceFileName: tarFile, destinationDirectoryName: destinationDirectory, overwriteFiles: true);

    if (Directory.Exists(destinationDirectory))
    {
        foreach (string dir in Directory.GetDirectories(destinationDirectory))
        {
            WriteInformation($&quot;Extracted directory {dir.Split(Path.DirectorySeparatorChar).Last()} containing these files: &quot; + string.Join(',', Directory.EnumerateFiles(dir).Select(file =&amp;gt; Path.GetFileName(file))));
        }
    }
}
catch (Exception ex)
{
    WriteError(ex.Message);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjqQZs/btsFcQSeQzG/kGwGwdzZGniTA2TKGYka1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjqQZs/btsFcQSeQzG/kGwGwdzZGniTA2TKGYka1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjqQZs/btsFcQSeQzG/kGwGwdzZGniTA2TKGYka1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjqQZs%2FbtsFcQSeQzG%2FkGwGwdzZGniTA2TKGYka1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;188&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 반디집과 같이 tar archive의 content를 볼 수 있는 software가 있다면 이 것을 사용해 images-archive.tar file의 content를 다음과 같이 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H3gYV/btsFcQklRPL/DjQtUciMO0WK5EJXfnNK7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H3gYV/btsFcQklRPL/DjQtUciMO0WK5EJXfnNK7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H3gYV/btsFcQklRPL/DjQtUciMO0WK5EJXfnNK7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH3gYV%2FbtsFcQklRPL%2FDjQtUciMO0WK5EJXfnNK7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;398&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● tar 항목 읽기 및 쓰기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TarFile class 외에도 tar 압축 file에서 개별적인 항목을 읽고 쓰기 위한 TarEntry, TarEntryFormat, TarReader, TarWriter 등의 class가 존재합니다. 이들은 GzipStream과 결합하여 쓰거나 읽을 때 항목을 압축하거나 해제할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET에서 지원하는 tar에 관한 더 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot;&gt;System.Formats.Tar Namespace | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689142814567&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;System.Formats.Tar Namespace&quot; data-og-description=&quot;Contains types used in reading and writing data in the Tape Archive (TAR) file archiving format.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fagHD/hyTimuysbV/D0pOn6MrBPnGK5OskEH9UK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fagHD/hyTimuysbV/D0pOn6MrBPnGK5OskEH9UK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;System.Formats.Tar Namespace&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contains types used in reading and writing data in the Tape Archive (TAR) file archiving format.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Random access handle을 사용한 읽고 쓰기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET의 처음 20년 동안은 file을 직접적으로 처리하는 유일한 API는 stream class였습니다. 이들은 data를 순차적으로 처리해야 하는 자동화된 작업에 적합합니다. 하지만 사람이 data와 상호작용하는 경우 종종 같은 위치로 여러번 이동하고 되돌아 와야할 때가 있습니다. 이에 따라 .NET 6부터는 file stream이 필요없이 file에 대해 직접적으로 작동하면서 randome으로 접근할 수 있는 새로운 API를 도입하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 예제를 만들어 보기 위해 아래와 같이 code를 작성합니다. 이 예제는 myTxt.txt라는 file에 대한 handle을 가져오도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708934236353&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.Win32.SafeHandles;
using System.Text;

using SafeFileHandle handle = File.OpenHandle(path: &quot;myTxt.txt&quot;, mode: FileMode.OpenOrCreate, access: FileAccess.ReadWrite);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런다음 file에 저장할 내용을 아래와 같이 작성합니다. 이 예제는 abcdefg라는 문자열을 byte array로 encoding하여 읽기전용 memory buffer에 담은 다음 file로 저장하는 동작을 수행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708934493219&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ReadOnlyMemory&amp;lt;byte&amp;gt; buffer = new(Encoding.UTF8.GetBytes(&quot;abcdefg&quot;));
await RandomAccess.WriteAsync(handle, buffer, fileOffset: 0);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;file을 읽기위해서는 우선 file의 길이(length)값을 가져온 다음 그 값을 통해 읽은 content를 담아둘 memory buffer를 할당합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708934681136&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long length = RandomAccess.GetLength(handle);
Memory&amp;lt;byte&amp;gt; contentBytes = new(new byte[length]);

await RandomAccess.ReadAsync(handle, contentBytes, fileOffset: 0);
string content = Encoding.UTF8.GetString(contentBytes.ToArray());
WriteLine($&quot;파일내용: {content}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sKwAa/btsFkVqUDF1/H2x69rnAU41iMHgi4ISKEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sKwAa/btsFkVqUDF1/H2x69rnAU41iMHgi4ISKEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sKwAa/btsFkVqUDF1/H2x69rnAU41iMHgi4ISKEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsKwAa%2FbtsFkVqUDF1%2FH2x69rnAU41iMHgi4ISKEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;172&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. Text의 encoding과 decoding&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text 문자는 다양한 방식으로 표현될 수 있습니다. 예를 들어 alphabet은 전신을 통해 전송할 수 있는 일련의 점과 선으로 구성되는 Morse 부호로 encode 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷하게 computer에서의 text도 code의 공간 안에서 code point를 나타내는 bit(1과 0)로 저장됩니다. 대부분의 code point는 단일 문자로 표현되지만 형식지정과 같은 다른 의미를 가질 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 ASCII는 128 code point를 가진 code공간을 가지고 있습니다. .NET은 Unicode라고 하는 표준을 통해 text를 내부적으로 endcode 합니다. Unicode는 백만 code point이상을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 text를 Unicode를 사용하지 않거나 Unicode의 변형을 사용하는 system에서 사용하기 위해 .NET 외부로 보내야 하는 경우가 있는데 이 때문에 encoding사이에 변환방법을 아는 것은 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표에서는 computer에서 일반적으로 사용되는 text ecoding을 나열하고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;Encoding&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;ASCII&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;byte중 하위 7bit만을 사용함으로서 문자의 범위가 제한적입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;UTF-8&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;각 Unicode code point를 1~4byte의 sequence로 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;UTF-7&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;UTF-8보다 7bit channel에서 더 효휼적으로 설계되었지만 보안과 견고함에서 문제점을 가지고 있으므로 UTF-7보다는 UTF-8을 더 권장합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;UTF-16&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;각 Unicode code point를 1~2개의 16bit integer로 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;UTF-32&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;각 Unicode code point를 32bit integer로 표현하므로 가변 길이 encoding의 다른 Unicode encoding과는 달리 고정된 길이의 encoding입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;ANSI/ISO encodings&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;특정 언어 또는 언어의 group을 지원하기 위해 사용되는 다양한 code page에 대한 지원을 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현시점에 대부분의 system은 UTF-8을 기본으로 사용하고 있으며 .NET에서는 Encoding.Default로 나타낼 수 있습니다. 상술하였듯 Encoding.UTF7은 보안에 취약하므로 사용을 피해야 하며 이러한 이유로 C# compiler는 UTF-7 사용을 시도할 때 경고를 띄우고 있습니다. UTF-7은 다른 system과의 호환성을 위해 어쩔 수 없이 text를 encoding 해야 하는 경우를 위해서 선택적으로 사용할 수 있도록 남아있을 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Byte array로 문자열 encoding 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy09 solution에서 WorkingWithEncodings이름의 project를 생성하고 Program.cs에서는 System.Text namespace를 import 한 뒤 사용자로 부터 선택된 encoding을 사용해 문자열을 encode 하는 문을 아래와 같이 작성합니다. 또한 각 byte는 loop를 통해 문자열로 decode 한 다음 이를 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689213097165&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WriteLine(&quot;Encodings&quot;);
WriteLine(&quot;[1] ASCII&quot;);
WriteLine(&quot;[2] UTF-7&quot;);
WriteLine(&quot;[3] UTF-8&quot;);
WriteLine(&quot;[4] UTF-16 (Unicode)&quot;);
WriteLine(&quot;[5] UTF-32&quot;);
WriteLine(&quot;[6] Latin1&quot;);
WriteLine(&quot;[any other key] Default encoding&quot;);
WriteLine();
            
Write(&quot;Press a number to choose an encoding.&quot;);
ConsoleKey number = ReadKey(intercept: true).Key;
WriteLine(); WriteLine();

Encoding encoder = number switch
{
    ConsoleKey.D1 or ConsoleKey.NumPad1 =&amp;gt; Encoding.ASCII,
    ConsoleKey.D2 or ConsoleKey.NumPad2 =&amp;gt; Encoding.UTF7,
    ConsoleKey.D3 or ConsoleKey.NumPad3 =&amp;gt; Encoding.UTF8,
    ConsoleKey.D4 or ConsoleKey.NumPad4 =&amp;gt; Encoding.Unicode,
    ConsoleKey.D5 or ConsoleKey.NumPㄴad5 =&amp;gt; Encoding.UTF32,
    ConsoleKey.D6 or ConsoleKey.NumPad6 =&amp;gt; Encoding.Latin1,
    _ =&amp;gt; Encoding.Default
};

// encode할 문자열
string message = &quot;Caf&amp;eacute; &amp;pound;4.39&quot;;
WriteLine($&quot;Text to encode: {message} Characters: {message.Length}&quot;);

// 문자열을 byte array로 encode
byte[] encoded = encoder.GetBytes(message);

// encode후 byte 확인
WriteLine(&quot;{0} used {1:N0} bytes.&quot;, encoder.GetType().Name,
encoded.Length);
WriteLine();

// 각 byte 열거
WriteLine($&quot;BYTE | HEX | CHAR&quot;);
foreach (byte b in encoded)
{
    WriteLine($&quot;{b,4} | {b.ToString(&quot;X&quot;),3} | {(char)b,4}&quot;);
}

string decoded = encoder.GetString(encoded);
WriteLine(decoded);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 ASCII선택을 위해 1을 누르면 아래와 같이 결과를 확인할 수 있습니다. 표시된 byte결과를 보면 pound 기호 (&amp;pound;)와 accent e (&amp;eacute;)는 ASCII로 표현되지 않았음을 확인할 수 있습니다. 따라서 이때는 ?문자가 대신 사용되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAzYHZ/btsFfcmrxeX/WzawePkVKWWD2rw4RUX9PK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAzYHZ/btsFfcmrxeX/WzawePkVKWWD2rw4RUX9PK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAzYHZ/btsFfcmrxeX/WzawePkVKWWD2rw4RUX9PK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAzYHZ%2FbtsFfcmrxeX%2FWzawePkVKWWD2rw4RUX9PK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;474&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 예제를 실행하고 이번에는 UTF-8인 3을 입력합니다. UTF-8에서는 위 두 문자를 위해 추가로 2 byte를 더 필요로 하지만(총 12byte)&amp;nbsp; &amp;eacute; 과 &amp;pound; 문자에 대한 encode와 decode가 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7syeD/btsFfWXXkMP/RaEyk5Zk5PBQWPOpjqzs11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7syeD/btsFfWXXkMP/RaEyk5Zk5PBQWPOpjqzs11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7syeD/btsFfWXXkMP/RaEyk5Zk5PBQWPOpjqzs11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7syeD%2FbtsFfWXXkMP%2FRaEyk5Zk5PBQWPOpjqzs11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;488&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UTF-16은 모든 문자에 대해 2byte를 필요로 하므로 총 20byte가 사용되며 &amp;eacute; 와 &amp;pound;문자 역시 encode와 decode가 가능합니다. 해당 encoding은 .NET에서 내부적으로 char와 string값을 저장하기 위해 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) File로 text를 encoding 하고 decoding 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StreamReader와 StreamWriter 같은 stream helper class를 사용할 때 사용하고자 하는 encoding을 지정할 수 있습니다. helper로 작성하면 text는 자동적으로 encoded 되며 helper로부터 읽으면 byte는 자동적으로 decode 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Encoding을 지정하려면 helper type의 생성자로 두 번째 매개변수를 통해 encoding을 다음과 같이 전달합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689214867957&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StreamReader reader = new(stream, Encoding.UTF8); 
StreamWriter writer = new(stream, Encoding.UTF8);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 다른 system에서 사용할 file을 생성하기 때문에 어떤 encoding을 사용할지 선택할 수 없는 경우가 있습니다. 만약 이런 상황이라면 가장 적은 수의 byte를 사용하되 필요한 모든 문자를 저장할 수 있는 것을 선택해 보는 것이 도움이 될 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. Random access handle을 가지는 읽기 및 쓰기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6부터는 file stream이 없이도 file을 대상으로 작업이 가능한 새로운 API를 도입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 우선 아래와 같이 file에 대한 handle을 가져와야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689232689457&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.Win32.SafeHandles;
using System.Text;

using SafeFileHandle handle = File.OpenHandle(path: &quot;test.txt&quot;, mode: FileMode.OpenOrCreate, access: FileAccess.ReadWrite);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하고 나면 byte array로 encode 된 text를 읽기 전용 memory buffer에 아래와 같이 작성하고 이를 file로 저장할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689232873056&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ReadOnlyMemory&amp;lt;byte&amp;gt; buffer = new(Encoding.UTF8.GetBytes(&quot;test123123&quot;));
await RandomAccess.WriteAsync(handle, buffer, fileOffset: 0);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;file에서 읽기 위해서는 file의 길이(크기)를 가져와 해당 길이만큼의 공간을 memory buffer에 할당하여 file을 읽어 들입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689233040103&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long length = RandomAccess.GetLength(handle);
Memory&amp;lt;byte&amp;gt; contentBytes = new(new byte[length]);
await RandomAccess.ReadAsync(handle, contentBytes, fileOffset: 0);

string content = Encoding.UTF8.GetString(contentBytes.ToArray());
WriteLine($&quot;Content of file: {content}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QHeWV/btsFeTUWyLL/SrFOP2QcZoX4nJXKkbKao0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QHeWV/btsFeTUWyLL/SrFOP2QcZoX4nJXKkbKao0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QHeWV/btsFeTUWyLL/SrFOP2QcZoX4nJXKkbKao0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQHeWV%2FbtsFeTUWyLL%2FSrFOP2QcZoX4nJXKkbKao0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;76&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. Object graph 직렬화&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object graph는 직접 참조 혹은 참조 chain을 통해 간접적으로 서로 연결된 다수의 개체입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직렬화는 지정한 형식을 사용해 실제 object graph를 일련의 byte로 변환하는 처리를 말합니다.(역직렬화는 이런 처리를 반대로 수행합니다.) 직렬화를 통해 우리는 실제 개체의 현재 상태를 저장할 수 있으며 이후에 그 상태 그대로 개체를 다시 재생할 수 있습니다. 예를 들어 대표적으로 game을 들 수 있는데 현재 game의 진행상태를 저장함으로서 우리는 이후에 마지막상태에서 game을 계속 진행할 수 있습니다. 직렬화된 개체는 database나 file로 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정할 수 있는 format(형식)에는 수십 가지가 있지만 대부분의 경우 XML(eXtensible Markup Language)나 JSON(JavaScript Object Notation)이 둘 중 하나를 사용합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;JSON은 data를 상당히 소형화할 수 있는 것으로 web이나 mobile application에서 최적화되어 있습니다. XML은 JSON보다 훨씬 장황하며 주로 legacy system에서 지원되는 경우가 많습니다. 따라서 직렬화된 개체 graph의 크기를 최소화하려면 JSON을 사용하는 것이 좋습니다. JSON은 태생적으로 JavaScript를 위한 직렬화형식이므로 개체 graph를 web이나 mobile application으로 전송하기에도 적합한 형식이라 할 수 있습니다. mobile app은 대게 제한된 대역폭환경에서 동작하는 경우가 많으므로 byte의 수는 중요한 요소로 작용합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 XML과 JSON으로 직렬화하고 그 반대를 수행할 수 있는 다수의 class를 가지고 있는데, 그중 XmlSerializer와 JsonSerializer가 가장 대표적인 class에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) XML 직렬화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 가장 많이 사용되는 직렬화 형식 중 하나가 바로 XML입니다. 일반적인 예제를 만들어 보기 위해 csStudy09 solution에서 WorkingWithSerialization이름의 project를 생성하고 자체 혹은 파생된 class에서만 접근할 수 있는 protected속성인 Group으로 Student.cs이름의 class file를 아래와 같이 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689301487187&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Student
{
    public Student(int group)
    {
        Group = group;
    }

    public string? Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public HashSet&amp;lt;Student&amp;gt;? Friends { get; set; }
    protected int Group { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 초기 salary값을 설정하기 위해 단일 매개변수를 가진 생성자를 사용하고 있습니다. Program.cs에서는 기존의 문을 모두 삭제하고 XML 직렬화에 필요한 정적 Environment와 Path namespace를 import 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689301614963&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.Environment;
using static System.IO.Path;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Student instance의 개체 graph를 생성하는 문을 아래와 같이 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689301910995&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Student&amp;gt; students = new()
{
    new(1)
    {
        Name = &quot;홍길동&quot;,
        DateOfBirth = new(year: 2014, month: 9, day: 14)
    },
    new(2)
    {
        Name = &quot;홍길영&quot;,
        DateOfBirth = new(year: 2014, month: 6, day: 29)
    },
    new(3)
    {
        Name = &quot;장진구&quot;,
        DateOfBirth = new(year: 2014, month: 3, day: 7),
        Friends = new()
        {
            new(4)
            {
                Name = &quot;김말순&quot;,
                DateOfBirth = new(year: 2014, month: 7, day: 7)
            }
        }
    }
};

// Person의 List를 XML로 형식화할 개체 생성
XmlSerializer xs = new(type: students.GetType());
            
// 작성할 file생성
string path = Combine(CurrentDirectory, &quot;students.xml&quot;);
using (FileStream stream = File.Create(path))
{
    // 개체 graph를 stream으로 직렬화
    xs.Serialize(stream, students);
}

Console.WriteLine(&quot;Written {0:N0} bytes of XML to {1}&quot;, arg0: new FileInfo(path).Length, arg1: path);
Console.WriteLine();
            
Console.WriteLine(File.ReadAllText(path));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아마도 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tw8o3/btsFeHfUCvN/53jn2Slljuu1BbKkPAUkKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tw8o3/btsFeHfUCvN/53jn2Slljuu1BbKkPAUkKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tw8o3/btsFeHfUCvN/53jn2Slljuu1BbKkPAUkKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftw8o3%2FbtsFeHfUCvN%2F53jn2Slljuu1BbKkPAUkKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;83&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 오류를 해결하기 위해서는 Student.cs에서 매개변수 없는 생성자를 아래와 같이 추가해줘야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689302608706&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Student() { }&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사실 위 생성자는 어디에서도 사용되지 않지만 반드시 존재해야 XmlSerializer가 새로운 Student instance를 역직렬화 처리 중에 호출할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 결과를 확인해 봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehUOS2/btsFdVTcfTM/V8MH873j11s5Bk4b8nXsNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehUOS2/btsFdVTcfTM/V8MH873j11s5Bk4b8nXsNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehUOS2/btsFdVTcfTM/V8MH873j11s5Bk4b8nXsNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehUOS2%2FbtsFdVTcfTM%2FV8MH873j11s5Bk4b8nXsNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;421&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과에서는 '&amp;lt;Name&amp;gt;홍길동&amp;lt;/Name&amp;gt;'과 같이 XML요소로 개체 graph가 직렬화되었으며 Group속성은 public속성이 아니기에 포함되지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) XML 경량화하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 일부 field는 요소대신 attribute를 사용해 더욱 경량화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Student.cs에서 System.Xml.Serialization namespace를 import한뒤&lt;/p&gt;
&lt;pre id=&quot;code_1689303015443&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Xml.Serialization;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Name, DateOfBirth속성에 해당 [XmlAttribute] attribute를 적용합니다. 이때 각 attribute에 아래와 같이 줄임말을 적용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689303192307&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[XmlAttribute(&quot;nm&quot;)]
public string? Name { get; set; }

[XmlAttribute(&quot;dob&quot;)]
public DateTime DateOfBirth { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 적용 후 다시 예제를 실행하면 설정한 줄임말이 사용됨으로써 file공간이 절약됨을 알 수 있습니다. 따라서 생성된 file의 용량이 이전보다 더 줄어들게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lgKYv/btsFf6sFXBE/YPtm1j1qLejwkJr8DS2QaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lgKYv/btsFf6sFXBE/YPtm1j1qLejwkJr8DS2QaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lgKYv/btsFf6sFXBE/YPtm1j1qLejwkJr8DS2QaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlgKYv%2FbtsFf6sFXBE%2FYPtm1j1qLejwkJr8DS2QaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;240&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) XML file 역직렬화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Memory에 실제 개체로 XML file을 역직렬화하기 위해 Program.cs에서 XML file을 열고 역직렬화하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689321412621&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine();
Console.WriteLine(&quot;* Deserializing XML files&quot;);

string path = Combine(CurrentDirectory, &quot;students.xml&quot;);

List&amp;lt;Student&amp;gt; students = new();
XmlSerializer xs = new(type: students.GetType());

using (FileStream xmlLoad = File.Open(path, FileMode.Open))
{
    List&amp;lt;Student&amp;gt;? loadedStudent = xs.Deserialize(xmlLoad) as List&amp;lt;Student&amp;gt;;
    if (loadedStudent is not null)
    {
        foreach (Student p in loadedStudent)
        {
            Console.WriteLine(&quot;{0} has {1} children.&quot;, p.Name, p.Friends?.Count ?? 0);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 XML로부터 student개체를 load 하여 아래와 같이 열거할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSZvVv/btsFdqlAW2N/A57bxYa6lQqo0uc2H9uq4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSZvVv/btsFdqlAW2N/A57bxYa6lQqo0uc2H9uq4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSZvVv/btsFdqlAW2N/A57bxYa6lQqo0uc2H9uq4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSZvVv%2FbtsFdqlAW2N%2FA57bxYa6lQqo0uc2H9uq4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;115&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 XML을 생성하는 데 사용할 수 있는 다른 많은 attribute들도 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 어떠한 annotation도 사용하지 않는다면 XmlSerializer는 역직렬화에서 속성의 이름에 대소문자를 구분하지 않고 수행합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;XmlSerializer를 사용할 때 오로지 public field와 속성만을 포함하며 type은 반드시 매개변수 없는 생성자를 가져야 합니다. 또한 attribute를 사용해 출력을 사용자정의할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) JSON 직렬화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Newtonsoft.Json는 Json.NET으로 알려져 있으며 JSON 직렬화에 사용할 수 있는 가장 인기 있고 강력한 .NET library 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NuGet package manager에서 download를 counter 하는 데 사용되는 단위는 32bit integer인데 Newtonsoft.Json의 인기가 너무 높은 나머지 여기서 수용가능한 숫자의 범위를 넘어설 정도의 download횟수가 기록된 적도 있을 정도입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Json.NET을 사용하기 위해 우선 Nuget Package에서 아래와 같이 Newtonsoft.Json을 검색해 최신 version을 내려받습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGVjID/btsFg5z03Ob/50POnOU31OHVXIsRzr1wC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGVjID/btsFg5z03Ob/50POnOU31OHVXIsRzr1wC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGVjID/btsFg5z03Ob/50POnOU31OHVXIsRzr1wC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGVjID%2FbtsFg5z03Ob%2F50POnOU31OHVXIsRzr1wC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1518&quot; height=&quot;998&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 text file을 생성하고 file에 JSON으로 student를 직렬화하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689322402094&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Student&amp;gt; students = new()
{
    new(1)
    {
        Name = &quot;홍길동&quot;,
        DateOfBirth = new(year: 2014, month: 9, day: 14)
    },
    new(2)
    {
        Name = &quot;홍길영&quot;,
        DateOfBirth = new(year: 2014, month: 6, day: 29)
    },
    new(3)
    {
        Name = &quot;장진구&quot;,
        DateOfBirth = new(year: 2014, month: 3, day: 7),
        Friends = new()
        {
            new(4)
            {
                Name = &quot;김말순&quot;,
                DateOfBirth = new(year: 2014, month: 7, day: 7)
            }
        }
    }
};

string jsonPath = Combine(CurrentDirectory, &quot;student.json&quot;);
using (StreamWriter jsonStream = File.CreateText(jsonPath))
{
    Newtonsoft.Json.JsonSerializer jss = new();
    jss.Serialize(jsonStream, students);
}

Console.WriteLine();
Console.WriteLine(&quot;Written {0:N0} bytes of JSON to: {1}&quot;, arg0: new FileInfo(jsonPath).Length, arg1: jsonPath);

Console.WriteLine(File.ReadAllText(jsonPath));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행해 보면 JSON은 요소로 이루어진 XML에 비해 byte의 수가 절반도 안 되는 훨씬 적은 용량만이 필요함을 알 수 있습니다. 심지어 attribute를 사용한 XML에 비해서도 더 작은 용량입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkQA2H/btsFeQDQMAP/uTIEMsOKVB5xJkxgSfjFIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkQA2H/btsFeQDQMAP/uTIEMsOKVB5xJkxgSfjFIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkQA2H/btsFeQDQMAP/uTIEMsOKVB5xJkxgSfjFIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkQA2H%2FbtsFeQDQMAP%2FuTIEMsOKVB5xJkxgSfjFIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;123&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;123&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 고성능 JSON 처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Core 3.0은 JSON처리에 필요한 새로운 namespace인 System.Text.Json를 추가하였으며 Span와 같은 API를 사용하여 성능에 최적화하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Json.NET과 같은 이전 library는 UTF-16을 읽음으로써 구현되는데 HTTP를 포함한 대부분의 network protocol이 UTF-8을 사용하고 Json.NET의 Unicode 문자열 값으로 부터 UTF-8로 변환하는 것을 피할 수 있기 때문에 UTF-8을 사용하여 JSON문서를 읽고 쓰는 것이 더 성능이 좋다고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Micorsoft는 이 새로운 API를 통해 상황에 따라 1.3 ~ 5배까지 성능향상을 달성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본래 Json.NET의 제작자인 James Newton-King은 그가 JSON API에 관한 comment에서 'Json.NET은 사라지지 않는다.'라고 언급한 것처럼 Microsoft와 협업하여 새로운 JSON type을 개발하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON type을 역직렬화하는데 새로운 JSON API를 사용하기 위해 WorkingWithSerialization project의 Program.cs상단에서 직렬화를 수행하기 위한 새로운 JSON class를 별칭을 사용해 Import 합니다. 별칭은 위에서 사용한 Json.NET의 이름과 충돌하는 것을 피하기 위한 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689406712245&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using FastJson = System.Text.Json.JsonSerializer;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 JSON file을 열어 역직렬화를 수행한 뒤 Student에 대한 frends의 이름과 수를 출력하도록 하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689406917468&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string jsonPath = Combine(CurrentDirectory, &quot;student.json&quot;);

using (FileStream jsonLoad = File.Open(jsonPath, FileMode.Open))
{
	// deserialize object graph into a List of Person
	List&amp;lt;Student&amp;gt;? loadedStudent = await FastJson.DeserializeAsync(utf8Json: jsonLoad, returnType: typeof(List&amp;lt;Student&amp;gt;)) as List&amp;lt;Student&amp;gt;;

	if (loadedStudent is not null)
	{
		foreach (Student s in loadedStudent)
		{
			Console.WriteLine(&quot;{0} has {1} children.&quot;, s.Name, s.Friends?.Count ?? 0);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KSunF/btsFg3IWSyt/sFeTuqlEtHh9zmg21MkGOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KSunF/btsFg3IWSyt/sFeTuqlEtHh9zmg21MkGOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KSunF/btsFg3IWSyt/sFeTuqlEtHh9zmg21MkGOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKSunF%2FbtsFg3IWSyt%2FsFeTuqlEtHh9zmg21MkGOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;134&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개발 생산성을 위해서는 Json.NET을, 다양한 기능 또는 성능을 위해서는 System.Text.Json을 사용합니다. 아래 link를 통해 이 둘에 대한 차이목록을 확인해 볼 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#table-of-differences-between-newtonsoftjson-and-systemtextjson&quot;&gt;Migrate from Newtonsoft.Json to System.Text.Json - .NET | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689407342019&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Migrate from Newtonsoft.Json to System.Text.Json - .NET&quot; data-og-description=&quot;Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#table-of-differences-between-newtonsoftjson-and-systemtextjson&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jtKgX/hyTk65BcZr/7Xkege2gF5vmKACtUMmdH1/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#table-of-differences-between-newtonsoftjson-and-systemtextjson&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#table-of-differences-between-newtonsoftjson-and-systemtextjson&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jtKgX/hyTk65BcZr/7Xkege2gF5vmKACtUMmdH1/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Migrate from Newtonsoft.Json to System.Text.Json - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) JSON 처리 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON처리 방식에 관해서는 아래와 같이 사용가능한 많은 option들이 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Field의 포함 또는 제외&lt;/li&gt;
&lt;li&gt;Casing 정책 설정&lt;/li&gt;
&lt;li&gt;대소문자 구분 정책 설정&lt;/li&gt;
&lt;li&gt;공백의 처리방식 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 위해 csStudy09 solution에서 WorkingWithJson이름의 project를 생성한 뒤 Program.cs에서 기존의 문을 모두 삭제하고 고성능 JSON을 위한 주요 namespace와 System. Environment, System.IO.Path namespace를 정적 import 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689472977408&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Text.Json; 

using static System.Environment;
using static System.IO.Path;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 Book.cs이라는 새로운 class file을 추가하고 그 안에 Book class를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689473150679&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Text.Json.Serialization;

namespace WorkingWithJson
{
	public class Book
	{
		public Book(string title)
		{
			Title = title;
		}

		public string Title { get; set; }

		public string? Author { get; set; }
		
		[JsonInclude]
		public DateTime PublishDate;

		[JsonInclude]
		public DateTimeOffset Created;

		public ushort Pages;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Program.cs로 돌아와 Book class의 instance를 생성하고 이를 JSON으로 직렬화하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689473595282&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Book mybook = new(title: &quot;어린왕자&quot;)
{
	Author = &quot;생텍쥐페리&quot;,
	PublishDate = new(year: 1943, month: 4, day: 6),
	Pages = 250,
	Created = DateTimeOffset.UtcNow,
};

JsonSerializerOptions options = new()
{
	IncludeFields = true, // 모든 field를 포함
	PropertyNameCaseInsensitive = true,
	WriteIndented = true,
	PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
	Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};

string filePath = Combine(CurrentDirectory, &quot;mybook.json&quot;);

using (Stream fileStream = File.Create(filePath))
{
	JsonSerializer.Serialize&amp;lt;Book&amp;gt;(utf8Json: fileStream, value: mybook, options);
}

Console.WriteLine(&quot;{0:N0} byte의 JSON file {1}&quot;, arg0: new FileInfo(filePath).Length, arg1: Path.GetFileName(filePath));
Console.WriteLine();
			
Console.WriteLine(File.ReadAllText(filePath));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mhdya/btsFfWXXlRO/uIw3B3k9qnZH86bSK9y5n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mhdya/btsFfWXXlRO/uIw3B3k9qnZH86bSK9y5n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mhdya/btsFfWXXlRO/uIw3B3k9qnZH86bSK9y5n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMhdya%2FbtsFfWXXlRO%2FuIw3B3k9qnZH86bSK9y5n1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;248&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 JSON file은 총 171byte로 member의 이름은 publishDate와 같이 camelCase가 적용되었습니다. 이는 Javascript를 사용하는 browser를 위한 가장 적합한 방식입니다. 또한 option설정에서 pages를 포함하여 모든 field를 포함하도록 했고 결과를 보면 사람이 읽기 쉽도록 하기 위해 JSON자체에 들여 쓰기 등의 서식이 적용되어 있음을 확인할 수 있습니다. DateTime와 DateTimeOffset값은 단일 표준 문자열 값으로 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 JsonSerializerOptions을 설정할 때 PropertNameCaseInsensitive, WriteIndented와 IncludeFields부분을 주서처리하고 예제를 다시 실행해 보면 결과를 다음과 같이 바뀌게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duxXzS/btsFg3oFvmq/Bu7WU9vtXaSfxcCmKOKM11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duxXzS/btsFg3oFvmq/Bu7WU9vtXaSfxcCmKOKM11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duxXzS/btsFg3oFvmq/Bu7WU9vtXaSfxcCmKOKM11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduxXzS%2FbtsFg3oFvmq%2FBu7WU9vtXaSfxcCmKOKM11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;134&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 JSON은 133byte가 되어 이전 171byte보다 용량이 작아졌으며 member의 이름은 normal casing가 사용되어 PublishDate와 같이 되었습니다. Pages field는 포함되지 않았으며 [JsonInclude]와 같은 attribute가 포함된 field는 정상적으로 포함되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) HTTP응답을 위한 새로운 JSON 확장 method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 5에서 Microsoft는 HttpResponse의 확장 method와 같이 System.Text.Json namespace type을 개선하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Newtonsoft에서 신규 JSON으로의 변환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Newtonsoft Json.NET library를 사용하는 code를 신규 System.Text.Json namespace로 변환해야 한다면 아래 link에서 Microsoft가 이를 위해 마련해 둔 문서를 찾아볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0&quot;&gt;Migrate from Newtonsoft.Json to System.Text.Json - .NET | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709089681352&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gwG1C/hyTjPY2Do7/32F9iUPgcPvUKFWML2U0fK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-description=&quot;Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json.&quot; data-og-title=&quot;Migrate from Newtonsoft.Json to System.Text.Json - .NET&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gwG1C/hyTjPY2Do7/32F9iUPgcPvUKFWML2U0fK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Migrate from Newtonsoft.Json to System.Text.Json - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. 환경변수&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수는 system및 사용자 정의가능한 값이며 이는 process의 동작을 실행하는 방식에 영향을 줄 수 있습니다. 일반적으로는 ASP.NET Core project에서 개발과 production 설정사이를 전환하기 위한 설정에 사용하거나 database연결 문자열에서 service key와 password같이 process에서 필요한 값을 전달하는데 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서 환경변수는 machine(system), user, process라는 3가지 범위에서 설정할 수 있습니다. 환경변수를 가져오거나 설정하는 method는 기본적으로 process의 범위 수준을 가정하며 process, user, machine의 EnvironmentVariableTarget을 지정하는 override를 가집니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 100px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 19.031%; height: 20px;&quot;&gt;GetEnvironmentVariables&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%; height: 20px;&quot;&gt;지정된 수준의 범위 혹은 현재 process에 대한 모든 환경변수의 IDictionary를 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 19.031%; height: 20px;&quot;&gt;GetEnvironmentVariable&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%; height: 20px;&quot;&gt;명몇된 환경변수의 값을 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 19.031%; height: 20px;&quot;&gt;SetEnvironmentVariable&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%; height: 20px;&quot;&gt;명명된 환경변수의 값을 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 19.031%; height: 40px;&quot;&gt;ExpandEnvironmentVariables&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%; height: 40px;&quot;&gt;문자열로 구성된 모든 환경변수, 예를 들어 &quot;Computer name :&amp;nbsp; %COMPUTER_NAME%&quot;에서와 같은 값에서 %%를 해당 식별되는 값으로 변환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 모든 환경 변수 가져오기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 다양한 범위 수준에서 현재 환경변수를 어떻게 확인할 수 있는지 확인해 보겠습니다. Program.cs를 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709095273213&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections;
using static System.Environment;

IDictionary evs = GetEnvironmentVariables();
IDictionary evm = GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
IDictionary evu = GetEnvironmentVariables(EnvironmentVariableTarget.User);

foreach (var key in evs.Keys)
{
    Console.WriteLine(&quot;key : {0}, value : {1}&quot;, key, evs[key]!.ToString());
}

Console.WriteLine(&quot;-------------------------------------------------------&quot;);

foreach (var key in evm.Keys)
{
    Console.WriteLine(&quot;key : {0}, value : {1}&quot;, key, evs[key]!.ToString());
}

Console.WriteLine(&quot;-------------------------------------------------------&quot;);

foreach (var key in evu.Keys)
{
    Console.WriteLine(&quot;key : {0}, value : {1}&quot;, key, evs[key]!.ToString());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 각각의 범위에 속한 모든 환경변수를 표시하도록 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kERiF/btsFm9bNhqM/TBSozh99FkA9Cl2R9EcDSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kERiF/btsFm9bNhqM/TBSozh99FkA9Cl2R9EcDSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kERiF/btsFm9bNhqM/TBSozh99FkA9Cl2R9EcDSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkERiF%2FbtsFm9bNhqM%2FTBSozh99FkA9Cl2R9EcDSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;856&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) 환경변수를 확장하고 설정하고 가져오기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 경우는 내장된 환경변수를 포함하고 있는 문자열을 형식화하여 표시해야 하는 경우가 있을 수 있습니다. 이러한 경우 환경변수는 %문자로 변수를 감싸는 형태로 표시됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;User : %USERNAME%, Computer : %COMPUTER_NAME%&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 환경변수는 Windows상에서 set 혹은 setx 명령을 통해 설정할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.8837%;&quot;&gt;변수 범위&lt;/td&gt;
&lt;td style=&quot;width: 75.1163%;&quot;&gt;예제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.8837%;&quot;&gt;Session/Shell&lt;/td&gt;
&lt;td style=&quot;width: 75.1163%;&quot;&gt;set MY_TMP_VAR=&quot;me&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.8837%;&quot;&gt;User&lt;/td&gt;
&lt;td style=&quot;width: 75.1163%;&quot;&gt;setx MY_TMP_VAR &quot;me&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.8837%;&quot;&gt;Machine&lt;/td&gt;
&lt;td style=&quot;width: 75.1163%;&quot;&gt;setx MY_TMP_VAR &quot;me&quot; /M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sDc2j/btsFp84il0r/sBAseaaxU07l8DgKjehTDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sDc2j/btsFp84il0r/sBAseaaxU07l8DgKjehTDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sDc2j/btsFp84il0r/sBAseaaxU07l8DgKjehTDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsDc2j%2FbtsFp84il0r%2FsBAseaaxU07l8DgKjehTDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;191&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set 명령은 임시 환경변수를 정의하는 것으로 즉시 값을 다시 읽거나 현재 shell 또는 session안에서 읽을 수 있습니다. 다만 해당 범위의 값을 설정할때는 위 표에서 처럼 =문자를 사용해 값을 할당함에 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setx 명령은 영구적인 환경변수를 설정하지만 현재 shell 또는 session을 닫고 재시작해야만 해당 환경변수를 읽을 수 있습니다. 또한 이 명령에서는 값의 할당을 위해 =문자를 사용하지 않음에 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setx명령에 관한 좀더 자세한 사항은 아래 link를 참고해 주시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot;&gt;setx | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709098054543&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;setx&quot; data-og-description=&quot;Reference article for the setx command, which creates or modifies environment variables in the user or system environment, without requiring programming or scripting.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9mg8A/hyVunKMZMm/mMkATHhvm8ANkVC3nPnjNK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9mg8A/hyVunKMZMm/mMkATHhvm8ANkVC3nPnjNK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;setx&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Reference article for the setx command, which creates or modifies environment variables in the user or system environment, without requiring programming or scripting.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 환경변수는 Windows상에서 Settings -&amp;gt; System -&amp;gt; About -&amp;gt; Advanced system settings -&amp;gt; System Properties 순서로 이동하면 해당 화면을 통해 변수항목과 해당 값을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beAGHv/btsFlSBHxcB/PXk5e29DgeIekYa8SJc39K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beAGHv/btsFlSBHxcB/PXk5e29DgeIekYa8SJc39K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beAGHv/btsFlSBHxcB/PXk5e29DgeIekYa8SJc39K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeAGHv%2FbtsFlSBHxcB%2FPXk5e29DgeIekYa8SJc39K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1261&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임시적으로 환경변수를 설정하는 것은 아래와 같이 export 명령으로도 가능합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;export MY_TMP_VAR=me&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export 명령에 대한 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ss64.com/bash/export.html&quot;&gt;export Man Page - Linux - SS64.com&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709098452905&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;export Man Page - Linux - SS64.com&quot; data-og-description=&quot;Set an environment variable. The supplied names are marked for automatic export to the environment of subsequently executed commands. If no names are supplied, or if the '-p' option is given, a list of exported names is displayed. To view the environment v&quot; data-og-host=&quot;ss64.com&quot; data-og-source-url=&quot;https://ss64.com/bash/export.html&quot; data-og-url=&quot;https://ss64.com/bash/export.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ss64.com/bash/export.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ss64.com/bash/export.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;export Man Page - Linux - SS64.com&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Set an environment variable. The supplied names are marked for automatic export to the environment of subsequently executed commands. If no names are supplied, or if the '-p' option is given, a list of exported names is displayed. To view the environment v&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ss64.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 알아본 방법외에 환경변수를 설정하고 값을 확인하고 확장하는 또 다른 방법이 존재하며 이를 예제를 통해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 환경변수를 포함하고 있는 문자열을 정의한 뒤 이에 대한 값을 출력하도록 아래와 같이 code를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709099236105&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string comUser = &quot;User name : %USERNAME%, Computer name : %COMPUTERNAME%&quot;;

Console.WriteLine(ExpandEnvironmentVariables(comUser));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnAys6/btsFmkSfL6z/MJd5KmS4wtKMutSxzKXqxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnAys6/btsFmkSfL6z/MJd5KmS4wtKMutSxzKXqxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnAys6/btsFmkSfL6z/MJd5KmS4wtKMutSxzKXqxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnAys6%2FbtsFmkSfL6z%2FMJd5KmS4wtKMutSxzKXqxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;172&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 MY_TMP_VAR 이름의 환경변수를 process범위로 설정하고 해당 값을 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709099496257&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string tmpKey = &quot;MY_TMP_VAR&quot;;
SetEnvironmentVariable(tmpKey, &quot;cliel&quot;);
string? tmpResult = GetEnvironmentVariable(tmpKey);

Console.WriteLine($&quot;{tmpKey}: {tmpResult}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Rsji/btsFob8kgtC/dklJnXYVbUnG4uKjz9HVfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Rsji/btsFob8kgtC/dklJnXYVbUnG4uKjz9HVfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Rsji/btsFob8kgtC/dklJnXYVbUnG4uKjz9HVfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Rsji%2FbtsFob8kgtC%2FdklJnXYVbUnG4uKjz9HVfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;153&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 MY_TMP_VAR 환경변수를 3개의 모든 범위수준에서 읽어오도록 program.cs를 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709100116168&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string tmpKey = &quot;MY_TMP_VAR&quot;;

string tmpValue = GetEnvironmentVariable(tmpKey, EnvironmentVariableTarget.Machine);
Console.WriteLine($&quot;Machine - {tmpKey}: {tmpValue}&quot;);

tmpValue = GetEnvironmentVariable(tmpKey, EnvironmentVariableTarget.User);
Console.WriteLine($&quot;User - {tmpKey}: {tmpValue}&quot;);

tmpValue = GetEnvironmentVariable(tmpKey, EnvironmentVariableTarget.Process);
Console.WriteLine($&quot;Process - {tmpKey}: {tmpValue}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런다음 project folder에서 다음과 같이 launchSettings.json file을 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709100745538&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;profiles&quot;: {
        &quot;test&quot;: {
            &quot;commandName&quot;: &quot;Project&quot;,
            &quot;environmentVariables&quot;: 
            {
                &quot;MY_TMP_VAR&quot;: &quot;me&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 test는 project 이름입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;terminal이나 command창에서는 다음 명령을 통해 user와 machine 두 범위에서 같은 이름의 환경변수를 설정합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;setx MY_TMP_VAR &quot;me1&quot;&lt;br /&gt;setx MY_TMP_VAR &quot;tmp2&quot; /M&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp5iaW/btsFoEoU1sS/ieIwCL44OUTECEMojDLC1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp5iaW/btsFoEoU1sS/ieIwCL44OUTECEMojDLC1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp5iaW/btsFoEoU1sS/ieIwCL44OUTECEMojDLC1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp5iaW%2FbtsFoEoU1sS%2FieIwCL44OUTECEMojDLC1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;267&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;권한관련 오류가 발생되는 경우 terminal을 관리자 권한으로 실행시켜야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행시켜 다음과 같은 결과를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBwySO/btsFm24S78n/5NvbtiUiJ9TI70lAWyw8Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBwySO/btsFm24S78n/5NvbtiUiJ9TI70lAWyw8Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBwySO/btsFm24S78n/5NvbtiUiJ9TI70lAWyw8Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBwySO%2FbtsFm24S78n%2F5NvbtiUiJ9TI70lAWyw8Y0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;210&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 통해 알 수 있듯이 launchSettings. json file에서의 설정은 process 수준으로 설정됩니다.&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>c#</category>
      <category>decoding</category>
      <category>encoding</category>
      <category>filesystem</category>
      <category>Serialization</category>
      <category>Stream</category>
      <category>환경변수</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/599</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-9-File-Streams-Serialization#entry599comment</comments>
      <pubDate>Wed, 28 Feb 2024 15:23:06 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 8. 공용 .NET Type</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-8-%EA%B3%B5%EC%9A%A9-NET-Type</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;.NET에는 일반적인 개발과정에서 사용할 수 있는 숫자, 문자열, collection등과 span, index, range와의 작업, network access 등 몇 가지 공용 type들을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 숫자 다루기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data에 관한 가장 일반적인 작업중 하나가 바로 숫자입니다. 아래표는 .NET에서 숫자에 관한 가장 일반적인 type을 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Example Type&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;SByte, Int16, Int32, Int64&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;정수로서 음수, 양수, 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Byte, UInt16, UInt32, UInt64&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;기수로서 0, 양수 / 부호가 없으므로 U로 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Half, Single, Double&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;실수로서, 부동소수점 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Decimal&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;실수(accurate real)로서 과학, 공학, 금융분야에서 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Numbers&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;BigInteger, Complex, Quaternion&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;임의의 큰 정수, 복소수, 4차원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 .NET Framework 1.0부터 32bit float과 64bit double type을 지원해 왔습니다. IEEE 754 명세는 또한 16bit floating-point 표준을 정의하였는데 Machine learning의 일부 algorithm에서 해당 type을 필요로 하므로 Microsoft는 System.Half type을 .NET 5부터 포함하였으며 .NET 7부터는 System.Int128와 System.UInt128 type을 포함하였습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재 C#언어는 half와 Int128, UInt128에 대한 별칭을 따로 정의하지 않았으므로 System.Half와 System.Int128, Sysem.UInt128을 그대로 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Big integer 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 별칭이 있는 .NET type에서 저장가능한 가장 큰 정수는 &lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;18조 5천억으로 unsigned ulong integer입니다. 만약 이것보다 더 큰 수를 저장해야 한다면 BigInteger를 사용해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서 csStudy08 solution을 만들고 여기에 WorkingWithNumbers이름의 Console App project를 추가합니다. 그런 뒤 Program.cs에서 기존구문을 모두 삭제하고 ulong type의 최댓값과 BigInteger를 통해 30 자릿수의 표시하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687936759756&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Numerics;

ulong big = ulong.MaxValue;
Console.WriteLine($&quot;{big,40:N0}&quot;);

BigInteger bigger = BigInteger.Parse(&quot;123456789012345678901234567890&quot;);
Console.WriteLine($&quot;{bigger,40:N0}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 사용한 format code 40은 오른쪽으로 40자리수 만큼 정렬하고자 하는 것이므로 두 숫자 모두 오른쪽 가장자리로 줄지어 표시하게 되며 N0은 천 단위 분리자와 소수점 0자리 사용을 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cR8qv6/btsE0h3bmwe/tkC5P5lkRrGNdp78i1Hmak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cR8qv6/btsE0h3bmwe/tkC5P5lkRrGNdp78i1Hmak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cR8qv6/btsE0h3bmwe/tkC5P5lkRrGNdp78i1Hmak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcR8qv6%2FbtsE0h3bmwe%2FtkC5P5lkRrGNdp78i1Hmak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;84&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 복소수 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복소수는 a + bi로 표현될 수 있으며 이때 a와 b는 실수, i는 허수 단위이고 여기서 i&amp;sup2; = -1입니다. 실수부 a가 0이면 순허수, 허수부 b가 0이면 이것은 실수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복소수는 많은 STEM(science, technology, engineering and mathematics)연구분야에서 실질적으로 응용되고 있으며 가수의 실수와 허수 부분이 별도로 더해짐으로써 추가되었습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 17px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 17px;&quot;&gt;(a + bi) + (c + di) = (a + c) + (b + d)i&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithNumbers project의 Program.cs에서 아래와 같이 2개의 복소수를 더하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688007502517&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Complex c1 = new(real: 4, imaginary: 2);
Complex c2 = new(real: 3, imaginary: 7);
Complex c3 = c1 + c2;

Console.WriteLine($&quot;{c1} added to {c2} is {c3}&quot;);
Console.WriteLine(&quot;{0} + {1}i added to {2} + {3}i is {4} + {5}i&quot;, c1.Real, c1.Imaginary, c2.Real, c2.Imaginary, c3.Real, c3.Imaginary);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beCOqs/btsE5Cr0e2g/3E1EMD1GTI4EyGZxmvq80K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beCOqs/btsE5Cr0e2g/3E1EMD1GTI4EyGZxmvq80K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beCOqs/btsE5Cr0e2g/3E1EMD1GTI4EyGZxmvq80K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeCOqs%2FbtsE5Cr0e2g%2F3E1EMD1GTI4EyGZxmvq80K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;92&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;..NET 6이전에는 (4, 2)와 (3, 7)을 더하면 (7, 9)가 되는 다른 형식을 사용했지만 .NET 7이후부터는 꺽쇠 괄호와 semi-colon을 사용하는 다른 기본형식을 사용합니다. 이는 일부 문화권에서 음수를 나타내기 위해 괄호를 사용하고 십진수를 나타내기 위해 commna를 사용하기 때문입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Quaternion(4원수)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Quaternions은 복소수를 확장자는 숫자체계로서 실수를 넘어 4차원 연관 규범 분할 대수를 형성하며 따라서 domain도 형성합니다. 사실 실제 활용해야 할 상황이 아니라면 이런 내용들에 대한 자세한 이해할 필요는 없는데, 이들은 공간을 묘사하는 데 사용되며 따라서 다수의 computer simulation과 비행 제어 system, video game engine에서 많이 사용되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 난수 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난수가 필요하지 않다면 Random class의 instance는 아래와 같이 생성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688021727236&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Random r = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Random class는 난수 생성의 초기화에 사용되는 seed 값을 지정하는 매개변수를 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688021952372&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Random r = new(Seed: 46378);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글에서 언급하였지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 2. C#&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708393225387&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 2. C#&quot; data-og-description=&quot;이번 글에서는 C# programming 언어에 대한 기본적인 사항을 살펴볼 것입니다. 전반적으로 알아야 할 용어와 C#에 대한 기본적인 문법에 대한 것들입니다. 1. C# 언어 C#을 통해 application에 대한 source co&quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vwcsG/hyVm1PBbMw/Zd6nvHrbh3y2I5hhGTlscK/img.png?width=681&amp;amp;height=109&amp;amp;face=0_0_681_109,https://scrap.kakaocdn.net/dn/gjfml/hyVmVhxpgI/QOY0V1KGz1s3rC3VaUocJ1/img.png?width=681&amp;amp;height=109&amp;amp;face=0_0_681_109,https://scrap.kakaocdn.net/dn/blZPRG/hyVmRMY4PF/Mbl1GmNbMlN0XZ8ZholPKk/img.png?width=1066&amp;amp;height=664&amp;amp;face=0_0_1066_664&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vwcsG/hyVm1PBbMw/Zd6nvHrbh3y2I5hhGTlscK/img.png?width=681&amp;amp;height=109&amp;amp;face=0_0_681_109,https://scrap.kakaocdn.net/dn/gjfml/hyVmVhxpgI/QOY0V1KGz1s3rC3VaUocJ1/img.png?width=681&amp;amp;height=109&amp;amp;face=0_0_681_109,https://scrap.kakaocdn.net/dn/blZPRG/hyVmRMY4PF/Mbl1GmNbMlN0XZ8ZholPKk/img.png?width=1066&amp;amp;height=664&amp;amp;face=0_0_1066_664');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 2. C#&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 C# programming 언어에 대한 기본적인 사항을 살펴볼 것입니다. 전반적으로 알아야 할 용어와 C#에 대한 기본적인 문법에 대한 것들입니다. 1. C# 언어 C#을 통해 application에 대한 source co&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수의 이름은 camel case로 사용하길 권장하고 있습니다. 하지만 예제에서 보듯 해당 class의 생성자를 정의한 개발자가 해당 규칙을 어기는 바람에 seed가 아닌 Seed로 사용되고 있습니다. 다시 말하지만 Seed가 아닌 seed가 되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Memory를 더 할당하지 않기 위해 .NET 6에서는 Random의 공유 정적 instance를 포함하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688023561036&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Random r = Random.Shared;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 Random 개체를 생성하게 되면 임의의 숫자를 생성하는 method를 아래와 같이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688023700785&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int ranInt = r.Next(minValue: 1, maxValue: 7); // 1에서 6사이값
double randomReal = r.NextDouble(); // 0.0에서 1.0보다 작은값
byte[] arrayOfBytes = new byte[256];
r.NextBytes(arrayOfBytes); //256 random byte array&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Random class는 random숫자를 생성하기 위해 일반적으로 사용되는 다음의 method를 갖고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3023%;&quot;&gt;Next&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%;&quot;&gt;Next method는 minValue와 maxValue 2개의 매개변수를 필요로 합니다. 다만 이때 maxValue값은 method가 반환하는 최댓값을 의미하는 것이 아님에 주의해야 합니다. 이는 'exclusive upper bound(배타적 상한선)'라고 하는 것으로 반환하는 최댓값보다 1 더 많아야 함을 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3023%;&quot;&gt;NextDouble&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%;&quot;&gt;0.0보다 더 크거나 같으면서 1.0미만의 수를 반환합니다. float type의 값이 필요하다면 NextSingle method를 대신 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3023%;&quot;&gt;NextBytes&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%;&quot;&gt;random byte (0 to 255)값으로 array를 채우는 method로서 00~FF와 같이 16진수로서 byte값을 형식화 하기 위해 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;암호화와 같이 진정한 random값이 필요한 경우를 위해 RandomNumberGenerator와 같은 특별한 type을 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 8에서는 다음과 같은 2개의 새로운 method를 포함하였습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.1628%;&quot;&gt;GetItems&amp;lt;T, length&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.8372%;&quot;&gt;해당 method에는 Type T에 대한 배열 혹은 읽기 전용 span과 생성하고자 하는 item의 수가 전달되며 이것으로 부터 ramdom하게 선택된 item을 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.1628%;&quot;&gt;Shuffle&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.8372%;&quot;&gt;해당 method에는 Type T에 대한 배열 혹은 일기 전용 span이 전달되며 해당 item의 순서가 ramdom하게 변경됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1708395121202&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Random r = Random.Shared;

string[] employees = r.GetItems(
    choices: new[] { &quot;lee&quot;, &quot;choi&quot;, &quot;kim&quot;, &quot;hong&quot; },
    length: 3);

foreach (string employee in employees)
    Console.Write($&quot; {employee}&quot;);

Console.WriteLine();

r.Shuffle(employees);

foreach (string emploee in employees)
    Console.Write($&quot; {emploee}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIXRfn/btsE6VLm9bU/K2cfKznGGDjuM2kV2AUcZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIXRfn/btsE6VLm9bU/K2cfKznGGDjuM2kV2AUcZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIXRfn/btsE6VLm9bU/K2cfKznGGDjuM2kV2AUcZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIXRfn%2FbtsE6VLm9bU%2FK2cfKznGGDjuM2kV2AUcZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;172&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) GUID 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUID (globally unique identifier)는 128bit TEXT 문자열로서 중복되지 않는 고유한 값을 나타냅니다. 따라서 multi-tasking환경에서의 충돌을 방지하는 것과 같은 상황에서 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Guid type은 값 type(구조체)로서 NewGuid method로 새로운 GUID값을 나타내며 기존 값을 문자열로서 나타내기 위한 Parse와 ParseTry method를 갖추고 있고 그 반대로 문자열을 GUID로 변환하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 위해 아래와 같이 NewGuid method로 GUID값을 생성하고 그 값을 byte array로 나타내기 위한 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708398121657&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Guid g = Guid.NewGuid();
Console.WriteLine($&quot;Random GUID: {g}.&quot;);

byte[] guidAsBytes = g.ToByteArray();

Console.Write(&quot;GUID to byte array: &quot;);
for (int i = 0; i &amp;lt; guidAsBytes.Length; i++)
    Console.Write($&quot;{guidAsBytes[i]:X2} &quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같이 표시할 것입니다. NewGuid method는 random한 값을 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LGHP1/btsEZnJHQAl/JekidKAgZAdj8qyB7ackb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LGHP1/btsEZnJHQAl/JekidKAgZAdj8qyB7ackb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LGHP1/btsEZnJHQAl/JekidKAgZAdj8qyB7ackb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLGHP1%2FbtsEZnJHQAl%2FJekidKAgZAdj8qyB7ackb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;153&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. Text&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text는 변수를 통해 사용되는 가장 일반적인 data type이라고 할 수 있습니다. 아래 표는 text와 관련된 .NET의 일반적인 type을 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 91px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Type&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Char&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;단일 한문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;String&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;System.Text&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;StringBuilder&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;고성능 문자열 다루기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;System.Text.RegularExpressions&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;Regex&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;고성능 문자열 pattern-matching&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 문자열 길이 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text를 다루게 되는 경우 사용되는 몇 가지 일반적인 상황들이 있습니다. 예를 들어 string변수에 저장된 문자열의 길이를 알아내는 경우 등이 그것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy08 solution에 WorkingWithText console app project를 추가하고 Program.cs에서 기본의 문을 모두 삭제한 뒤 도시 이름을 저장하기 위한 변수를 정의하고 console을 통해 도시이름과 길이를 출력하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688027234134&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string city = &quot;Seoul&quot;;
Console.WriteLine($&quot;{city} is {city.Length} characters long.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/een6bx/btsE8A7MlVJ/wTJ2QuJ2QAJ1uVdGq4wCu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/een6bx/btsE8A7MlVJ/wTJ2QuJ2QAJ1uVdGq4wCu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/een6bx/btsE8A7MlVJ/wTJ2QuJ2QAJ1uVdGq4wCu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feen6bx%2FbtsE8A7MlVJ%2FwTJ2QuJ2QAJ1uVdGq4wCu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;71&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 문자열에서 문자 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String class는 문자열을 저장하기 위해 내부적으로 문자에 대한 array를 사용하며 또한 한문자를 읽기 위해 array 구문을 사용할 수 있는 indexer를 가지고 있습니다. Array indexer는 0부터 시작하므로 세 번째 문자라면 index는 2가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 string변수의 첫 번째 문자와 네 번째 문자를 표시하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688032138696&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;First char is {city[0]} and fourth is {city[3]}.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wHnJf/btsE6QXy7Ou/yreQ16TW295b6hx2nue4iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wHnJf/btsE6QXy7Ou/yreQ16TW295b6hx2nue4iK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wHnJf/btsE6QXy7Ou/yreQ16TW295b6hx2nue4iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwHnJf%2FbtsE6QXy7Ou%2FyreQ16TW295b6hx2nue4iK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;76&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 문자열 분할&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 문자열 안에서 특정 문자(이를 테면 comma(,)와 같은)를 기준으로 array형태의 분리가 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 comma로 분리된 도시이름을 포함한 단일 문자열 변수를 정의하고 이를 Split method를 통해 분리하는 문을 추가합니다. 이때 분리의 기준을 comma로 지정하며 반환된 문자열값의 array를 열거하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688032466415&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string cities = &quot;Seoul,Busan,Daegu,Daejeon,Jeju&quot;;
string[] citiesArray = cities.Split(',');

Console.WriteLine($&quot;There are {citiesArray.Length} items in the array:&quot;);

foreach (string item in citiesArray)
{
    Console.WriteLine(item);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b43ttZ/btsE8DpTTbJ/wDWF7pGj9gbB1FXd9rQDQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b43ttZ/btsE8DpTTbJ/wDWF7pGj9gbB1FXd9rQDQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b43ttZ/btsE8DpTTbJ/wDWF7pGj9gbB1FXd9rQDQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb43ttZ%2FbtsE8DpTTbJ%2FwDWF7pGj9gbB1FXd9rQDQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;146&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 문자열의 일부 가져오기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열의 일부를 가져오는 방법 중의 하나로 IndexOf method를 사용할 수 있습니다. 해당 method는&amp;nbsp; 문자열 안에서 지정한 문자 또는 문자열에 대한 index위치를 반환하는 9개의 overload를 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Substring method는 2개의 다음과 같은 2개의 overload를 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Substring(startIndex, length) : startIndex로 시작하고 다음 length 문자만큼을 포함하는 문자열의 일부를 반환합니다.&lt;/li&gt;
&lt;li&gt;Substring(startIndex) : startIndex로 시작하고 그다음 끝까지 모든 문자열을 포함하는 문자열의 일부를 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 사람의 이름을 저장하는 string변수를 추가합니다. 이때 성과 이름은 공백으로 구분합니다. 그리고 해당 공백의 위치를 찾아 성과 이름을 분리하며 다른 순서로도 재결합될 수 있도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688095497910&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string fullName = &quot;hong seojun&quot;;
int indexOfTheSpace = fullName.IndexOf(' ');

string firstName = fullName.Substring(startIndex: 0, length: indexOfTheSpace);
string lastName = fullName.Substring(startIndex: indexOfTheSpace + 1);

Console.WriteLine($&quot;Original: {fullName}&quot;);
Console.WriteLine($&quot;Swapped: {lastName}, {firstName}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIiKLb/btsE2DLtHBz/DqLsJZDpBptzIU6lhUH4Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIiKLb/btsE2DLtHBz/DqLsJZDpBptzIU6lhUH4Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIiKLb/btsE2DLtHBz/DqLsJZDpBptzIU6lhUH4Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIiKLb%2FbtsE2DLtHBz%2FDqLsJZDpBptzIU6lhUH4Tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;85&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 내용에서 문자열 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열에서는 특정 문자열로 시작하는지 혹은 끝나는지, 아니면 어떤 문자열을 포함하고 있는지를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 문자열 저장을 위한 string변수를 추가하고 특정 문자열로 시작하는지와 포함하는지를 확인하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688107876663&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string company = &quot;Microsoft&quot;;
bool startsWithM = company.StartsWith(&quot;M&quot;);
bool containsN = company.Contains(&quot;N&quot;);

Console.WriteLine($&quot;Text: {company}&quot;);
Console.WriteLine($&quot;Starts with M: {startsWithM}, contains an N: {containsN}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c94Cip/btsE6EC6jzP/FVNkykO0q0DZLZSdf2XYdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c94Cip/btsE6EC6jzP/FVNkykO0q0DZLZSdf2XYdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c94Cip/btsE6EC6jzP/FVNkykO0q0DZLZSdf2XYdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc94Cip%2FbtsE6EC6jzP%2FFVNkykO0q0DZLZSdf2XYdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;85&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 문자열 비교&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 비교 역시 문자열을 통한 가장 일반적인 작업에 해당합니다. 예를 들어 login해야 하는 사용자가 자신의 ID와 비밀번호를 입력했다면 이것을 저장된 것과 비교해야할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string class는 IComparable interface를 구현하고 있으므로 CompareTo instance method를 통해 문자열을 비교할 수 있고 이때 문자열 값이 더 큰지, 같은지, 아니면 더 작은지에 따라 method는 -1, 0, 1의 값을 반환하게 됩니다. IComparable interface는 이미 아래 글에게 다루어본바 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 6. Interface와 Class상속&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708411623363&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 6. Interface와 Class상속&quot; data-og-description=&quot;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다. 또한 generic을 사용하여 어떻게 code를 안전하게 만들고 &quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dANDYX/hyVm2uhJKN/n45EsLyezwLjuaq6kC1t1K/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bc1t20/hyVmUQAchu/aLROBouOJiQdlZWFlJKPv0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bgnSPn/hyVmQHpODZ/3Ye7TZoAQE6lJeTJcLJ1BK/img.png?width=360&amp;amp;height=214&amp;amp;face=0_0_360_214&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dANDYX/hyVm2uhJKN/n45EsLyezwLjuaq6kC1t1K/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bc1t20/hyVmUQAchu/aLROBouOJiQdlZWFlJKPv0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bgnSPn/hyVmQHpODZ/3Ye7TZoAQE6lJeTJcLJ1BK/img.png?width=360&amp;amp;height=214&amp;amp;face=0_0_360_214');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 6. Interface와 Class상속&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다. 또한 generic을 사용하여 어떻게 code를 안전하게 만들고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 영문 대소문자는 순서에 영향을 줄 수 있고 문자열의 순서규칙은 문화권에 영향을 받습니다. 예를 들어 이중 L은&amp;nbsp; Spain어에서 하나의 문자로 취급됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일관성을 위해 때로는 문화권에 영향을 받지 않는 방식으로 비교를 수행해야할 필요가 있으며 이때 Compare라는 정적 method를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708412615783&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Globalization;

Console.OutputEncoding = System.Text.Encoding.UTF8;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(&quot;en-US&quot;);

string text1 = &quot;abc&quot;;
string text2 = &quot;ABC&quot;;

Console.WriteLine($&quot;문자열1: {text1}, 문자열2: {text2}&quot;);
Console.WriteLine(&quot;비교: {0}.&quot;, string.Compare(text1, text2));
Console.WriteLine(&quot;비교 (대소문자 무시): {0}.&quot;, string.Compare(text1, text2, ignoreCase: true));
Console.WriteLine(&quot;비교 (문화권및 대소문자 무시): {0}.&quot;, string.Compare(text1, text2, StringComparison.InvariantCultureIgnoreCase));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btgJvT/btsE9Zmjanh/Xs0xp7m5a5i66FSVP2MraK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btgJvT/btsE9Zmjanh/Xs0xp7m5a5i66FSVP2MraK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btgJvT/btsE9Zmjanh/Xs0xp7m5a5i66FSVP2MraK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtgJvT%2FbtsE9Zmjanh%2FXs0xp7m5a5i66FSVP2MraK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;210&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소문자 a는 대문자 A보다 더 작게 판단되므로 비교 결과가 -1이 되지만 대소문자를 무시하도록 하면 또는 더 확실하게 대소문자와 문화권을 무시하도록 설정하면 2개의 문자열을 동등하게 비교할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 비교에 관해서는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot;&gt;Collation, sorting, and string comparison - Globalization | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708412898933&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Collation, sorting, and string comparison - Globalization&quot; data-og-description=&quot;In world-ready applications, the alphabetical order can vary among languages, and the conventions for sequencing items can also be quite different.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pacAo/hyVmUJNTPe/Yx0gOcRHXaKp0oZQILJKYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pacAo/hyVmUJNTPe/Yx0gOcRHXaKp0oZQILJKYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Collation, sorting, and string comparison - Globalization&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In world-ready applications, the alphabetical order can vary among languages, and the conventions for sequencing items can also be quite different.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 문자열 결합, 형식화 그리고 기타&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표에서는 string에서 사용할 수 있는 몇 가지 member를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 294px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 17px;&quot;&gt;Member&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;Trim, TrimStart, TrimEnd&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;해당 method는 space나 tab 그리고 carriage return과 같은 공백문자를 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 20px;&quot;&gt;ToUpper, ToLower&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 20px;&quot;&gt;대상 문자열을 전부 대문자 혹은 소문자로 변환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 20px;&quot;&gt;Insert, Remove&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 20px;&quot;&gt;특정 문자열을 삽입하거나 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 20px;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 20px;&quot;&gt;특정 문자열을 다른 문자열로 대체합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;string.Empty&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;빈문자열를 표시하기 위해 매번 2중쌍따옴표(&quot;)를 사용하는 대신 공백을 표현하는데 사용될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;string.Concat&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;두 문자열 변수를 연결합니다. +연산자를 피연산자인 문자열 사이에 사용해도 동일한 결과를 얻을 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;string.Join&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;하나 또는 그 이상의 문자열을 각각의 문자열 변수 사이에 문자를 통해 연결합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;string.IsNullOrEmpty&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;string변수가 null이거나 비어있는지를 확인합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 17px;&quot;&gt;string.IsNullOrWhitespace&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 17px;&quot;&gt;string변수가 null이거나 공백인지를 확인합니다. 이때 공백은 tab, space, carriage return, line feed등을 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.4186%;&quot;&gt;string.Format&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%;&quot;&gt;형식화된 문자열변수를 출력하기 위한 보간문자열의 대체 method로 명명된 매개변수대신 위치를 사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 method 중 'string.'으로 표시된 method는 static method입니다. 즉, instance변수가 아닌 type에서부터 바로 호출될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 문자열값의 배열을 가져와 Join method를 통해 분리 문자열을 사용하여 단일 문자열로 결하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688109825956&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string cities = &quot;Seoul,Busan,Daegu,Daejeon,Jeju&quot;;
string[] citiesArray = cities.Split(',');
string recombined = string.Join(&quot; =&amp;gt; &quot;, citiesArray);

Console.WriteLine(recombined);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daf6eJ/btsEZCs0QO7/zUO0in8DIPXU2TcMUFzwYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daf6eJ/btsEZCs0QO7/zUO0in8DIPXU2TcMUFzwYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daf6eJ/btsEZCs0QO7/zUO0in8DIPXU2TcMUFzwYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdaf6eJ%2FbtsEZCs0QO7%2FzUO0in8DIPXU2TcMUFzwYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;70&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위치 매개변수와 보간 문자열 서식 구문을 사용해 같은 세 개의 변수를 두 번 표시하도록 하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688110092636&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string fruit = &quot;Apples&quot;;
int price = 2000;

DateTime when = DateTime.Today;

Console.WriteLine($&quot;Interpolated: {fruit} cost {price:C} on {when:dddd}.&quot;);
Console.WriteLine(string.Format(&quot;string.Format: {0} cost {1:C} on {2:dddd}.&quot;, arg0: fruit, arg1: price, arg2: when));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vYNxA/btsE6Gt7BFs/9AFdIZisIejWM9aYxDrrr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vYNxA/btsE6Gt7BFs/9AFdIZisIejWM9aYxDrrr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vYNxA/btsE6Gt7BFs/9AFdIZisIejWM9aYxDrrr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvYNxA%2FbtsE6Gt7BFs%2F9AFdIZisIejWM9aYxDrrr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;82&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;JetBrains Rider와 같은 일부 편집기에서는 boxing연산에 대한 경고문구를 표시할 수 있으나 지금 상황에서는 무시해도 딥니다. 만약 이러한 경고문구를 보지 않게 하고자 한다면 price와 when에서 ToString() method를 호출하면 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Console.WriteLine method는 string.Format와 동일한 서식을 지원하고 있기 때문에 좀 더 문을 간소화할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688110340388&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;WriteLine: {0} cost {1:C} on {2:dddd}.&quot;, arg0: fruit, arg1: price, arg2: when);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) 문자열을 효휼적으로 다루기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String.Concat method나 혹은 간단히 + 연산자를 사용하면 2개의 문자열을 결합해 새로운 문자열을 만들 수 있습니다. 하지만 이런 식의 문자열결합은 .NET이 memory에 완전히 새로운 문자열을 생성해야 하므로 좋지 않은 방법이라 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 대게는 단순히 2개의 문자열을 결합하는 경우라면 큰 문제가 없을 테지만 반복문안에서 많은 수의 반복으로 문자열결합을 구현하는 경우라면 성능면에서 부정적인 영향을 줄 수 있으며 memory낭비 역시 초래할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다수의 문자열을 많이 결합해야 하는 경우라면 StringBuilder type을 사용하는 것이 좋습니다. 이에 관한 내용은 아래 link을 참고해 주시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0#examples&quot;&gt;StringBuilder Class (System.Text) | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1688110837289&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;StringBuilder Class (System.Text)&quot; data-og-description=&quot;Represents a mutable string of characters. This class cannot be inherited.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0#examples&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/chyhQR/hyTa8Jvb85/aWKY2uMJkIgsBI40Kr4KjK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0#examples&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0#examples&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/chyhQR/hyTa8Jvb85/aWKY2uMJkIgsBI40Kr4KjK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;StringBuilder Class (System.Text)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Represents a mutable string of characters. This class cannot be inherited.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 정규 표현식을 통한 pattern matching&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 정규 표현식은 사용자로부터의 입력을 검증하는 데 사용되곤 합니다. 정규표현식은 아주 강력하지만 동시에 매우 복잡해질 수 있습니다. 거의 모든 programming언어가 정규표현식을 지원하고 있으며 이를 정의하기 위해 공통적인 특별한 문자들을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표현식의 예를 만들어 보기 위해 csStudy08 solution에서 WorkingWithRegularExpressions이름의 Console App project를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 문자열로서 입력된 숫자 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 입력을 검증하는 일반적인 예를 구현하는 것으로 시작해 보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithRegularExpressions&amp;nbsp;project의 Program.cs에서 사용자에게 나이입력을 요청하고 입력된 값에서 숫자문자를 찾는 정규표현식을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688192223597&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Text.RegularExpressions;

Console.Write(&quot;Enter your age: &quot;);

string input = Console.ReadLine()!;

Regex ageChecker = new(@&quot;\d&quot;);

if (ageChecker.IsMatch(input))
{
	Console.WriteLine(&quot;Thank you!&quot;);
}
else
{
	Console.WriteLine($&quot;This is not a valid age: {input}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 아래 사항에 주목해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@문자는 문자열 안에서 escape문자를 사용할 수 있는 기능을 끄는데, escape문자 앞에는 backslash가 붙습니다. 예를 들어 \t는 tab을 의미하며 \n은 new line을 의미합니다. 표현식을 사용할 때는 이 기능을 바꿔 사용해야 하므로 disable 해야 합니다.&lt;/li&gt;
&lt;li&gt;Escape문자가 @문자를 통해 비활성화되면 정규표현식으로 해석될 수 있습니다. 예를 들어 \d는 숫자를 의미하게 되는 것입니다. 이 부분에 관해서는 잠시 후 더 자세히 알아볼 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 나이를 묻게 되고 숫자를 입력하면 다음과 같은 결과가 표시될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8TTEE/btsEZAva73c/ei7okK2gyZB2EQ3Zbqd0xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8TTEE/btsEZAva73c/ei7okK2gyZB2EQ3Zbqd0xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8TTEE/btsEZAva73c/ei7okK2gyZB2EQ3Zbqd0xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8TTEE%2FbtsEZAva73c%2Fei7okK2gyZB2EQ3Zbqd0xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;115&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 숫자가 아닌 다른 형태로 값을 입력하게 되면 결과는 달라지게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1942&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bANwuq/btsEZnXcBG6/XprjObB1J1Ux8EMFKWyuyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bANwuq/btsEZnXcBG6/XprjObB1J1Ux8EMFKWyuyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bANwuq/btsEZnXcBG6/XprjObB1J1Ux8EMFKWyuyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbANwuq%2FbtsEZnXcBG6%2FXprjObB1J1Ux8EMFKWyuyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1942&quot; height=&quot;115&quot; data-origin-width=&quot;1942&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 숫자와 다른 문자를 섞어 입력해 봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BvLna/btsEZBHF4Ae/LqF0yRxWhssVAEzXRNrd6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BvLna/btsEZBHF4Ae/LqF0yRxWhssVAEzXRNrd6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BvLna/btsEZBHF4Ae/LqF0yRxWhssVAEzXRNrd6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBvLna%2FbtsEZBHF4Ae%2FLqF0yRxWhssVAEzXRNrd6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1474&quot; height=&quot;96&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식에서는 \d를 사용했습니다. 이는 숫자 하나를 의미합니다. 그러나 한자리 숫자 앞뒤에 무엇이 입력될 수 있는지는 명시하지 않았습니다. 해당 정규표현식을 풀어서 설명하면 '하나의 숫자를 입력하는 한 어떤 문자를 입력해도 상관없다.'가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식에서는 ^문자를 통해 시작되어야 하는 값을 나타낼 수 있고 $문자를 통해서는 끝나냐 하는 값을 나타낼 수 있습니다. 예제에서는 이들 기호문자를 사용해 숫자를 제외하고 입력된 값 앞뒤에 아무것도 없음을 아래와 같이 표시하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688193941542&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex ageChecker = new(@&quot;^\d+$&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 예제를 다시 실행하면 어떤 길이든지 0과 정수만 허용하게 됨을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 정규 표현식 성능 향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식 사용을 위한 .NET type은 .NET platform전역에서 사용될 수 있으며 다수의 app이 이를 내장하고 있습니다. 따라서 이들은 성능에 중요한 영향력을 가지고 있지만 Microsoft로 부터의 최적화에 대한 관심을 받지 못했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 5 이후에 들어와서는 System.Text.RegularExpressions namespace가 최대의 성능을 이끌어 내기 위해 내부적으로 재작성되으며 IsMatch와 같은 method를 사용한&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 일반적인 정규표현식 &lt;/span&gt;benchmark에서는 5배 정도 빠르게 측정되었습니다. 게다가 이러한 성능적 혜택을 누리기 위해 기존에 작성되었던 code를 다시 바꿀 필요가 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서 Regex class의 IsMatch method는 성능향상을 위한 ReadOnlySpan&amp;lt;char&amp;gt;을 전달할 수 있는 overload를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 정규 표현식 문법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 정규표현식에서 사용할 수 있는 일반적인 기호문자를 정리한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기호&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;의미&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기호&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;^&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;시작값&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;$&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;끝값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\d&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;숫자&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\D&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;비숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\s&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;공백&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\S&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;비공백&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\W&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;단어 문자&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\W&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;비단어 문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[A-Za-z0-9]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;문자의 범위&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\^&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;^(caret) 문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[aeiou]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;가능한 문자집합&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[^aeiou]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;비가능한 문자집합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;.&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;모든 단일문자&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\.&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;.(dot) 문자&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 아래는 정규표현식에서 이전 기호에 영향을 주는 일부 정규표현식 한정자를 정리한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기호&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;의미&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기호&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;+&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;하나 또는 그 이상&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;?&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;하나 아니면 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;{3}&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;정확히 3개&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;{3,5}&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;3~5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;{3,}&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;최소3개&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;{,3}&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;3이상&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 정규 표현식 예제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표에서는 정규식을 통한 몇 가지 예를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 220px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;표현식&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;\d&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;한자리 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;a&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;문자 a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;cliel&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;단어 cliel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^cliel&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;cliel로 시작하는 단어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;cliel$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;cliel로 끝나는 단어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^\d{2}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;정확히 2자리 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^[0-9]{2}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;위와 동일, 정확히 2자리 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^[A-Z]{4,}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;ASCII 문자 set에서의 최소 4개의 영문 대문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^[A-Za-z]{4,}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;ASCII 문자 set에서의 최소 4개의 영문 대소문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^[A-Z]{2}\d{3}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;ASCII 문자 set에서의 2개 대문자와 3개의 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.8372%;&quot;&gt;^[A-Za-z\u00c0-\ u017e]+$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%;&quot;&gt;ASCII 문자 set에서의 최소 1개의 대소문자 혹은 Unicode 문자 set에서의 European 문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.8372%;&quot;&gt;^d.g$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%;&quot;&gt;문자 d로 시작, 모든 문자, g로 끝나는 경우이므로 dig, dog처럼 d와 g사이에 단일문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.8372%;&quot;&gt;^d\.g$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%;&quot;&gt;문자 d로 시작, ., g로 끝나는 경우이므로 d.g에서만 일치&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;정규표현식은 거의 공통이므로 한번 사용한 정규표현식은 JavaScript나 Python과 같은 다른 언어에서 재사용이 가능합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 쉼표(,)로 구분된 문자열의 분리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 '문자열 분리'에서는 comma(,)로 구분된 문자열 변수에서 문자열을 어떻게 분리할 수 있을지에 대한 예제를 만들어본 바 있습니다. 그런데 아래와 같은 경우는 위에서 처럼 단순하게 처리할 수는 없는 형태입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&quot;aaa,bbb&quot;,&quot;ccc,ddd&quot;,&quot;eee,fff&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문자열값은 각각에서 큰따옴표(&quot;)를 사용하고 있으며 해당 값들은 쉼표(,)로 분리할 필요가 있는지 여부를 확인하기 위해 사용할 것입니다. 문자열 분리를 위한 Split method가 존재하지만 해당 예제에서는 맞지 않으므로 정규표현식을 대신 사용해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 link는 해당 예제를 작성하는데 영향을 받게 된 Stack Overflow의 article로서 더 자세한 설명을 확인해 볼 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string문자열 안에서 큰따옴표(&quot;)를 포함시키기 위해 이들 앞에 backslash를 붙여주거나 C# 11부터 도입된 'raw string literal'을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 comma로 구분된 문자열값을 저장하는 변수를 추가하고&amp;nbsp; Split method를 통해 문자열을 나누어 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688279031503&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string str = &quot;&quot;&quot;
&quot;aaa,bbb&quot;,&quot;ccc,ddd&quot;,&quot;eee,fff&quot;
&quot;&quot;&quot;;

Console.WriteLine($&quot;Strings to split: {str}&quot;);

string[] attrStr = str.Split(',');

Console.WriteLine(&quot;Splitting with string.Split method:&quot;);
foreach (string s in attrStr)
{
	Console.WriteLine(s);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 해당 문자열을 다시 정규표현식을 통해 나누는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688281231450&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex csv = new(&quot;(?:^|,)(?=[^\&quot;]|(\&quot;)?)\&quot;?((?(1)[^\&quot;]*|[^,\&quot;]*))\&quot;?(?=,|$)&quot;);
MatchCollection colStr = csv.Matches(str);

Console.WriteLine(&quot;Splitting with regular expression:&quot;);
foreach (Match s in colStr)
{
	Console.WriteLine(s.Groups[2].Value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHO7YI/btsE0I7k0HC/wmMzxMfKd6kylxi5Brqim0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHO7YI/btsE0I7k0HC/wmMzxMfKd6kylxi5Brqim0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHO7YI/btsE0I7k0HC/wmMzxMfKd6kylxi5Brqim0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHO7YI%2FbtsE0I7k0HC%2FwmMzxMfKd6kylxi5Brqim0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;286&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 정규표현식 구문강조 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022를 사용한다면 위에서 정규표현식을 사용하고자 생성자값으로 문자열을 전달할 때 색상으로 구문이 강조되어 있음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;19&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehAPTV/btsEZyD9pDV/Ubk0SpHVA6tiaps2Y6MPnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehAPTV/btsEZyD9pDV/Ubk0SpHVA6tiaps2Y6MPnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehAPTV/btsEZyD9pDV/Ubk0SpHVA6tiaps2Y6MPnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehAPTV%2FbtsEZyD9pDV%2FUbk0SpHVA6tiaps2Y6MPnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;598&quot; height=&quot;19&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;19&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 문자열값을 처리할 때 적용되지 않던 색상 구문 강조가 정규 표현식을 사용할 때 적용된 이유는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유를 알아보기 위해 생성자에서 mouse오른쪽 button을 click 한 뒤 'Go To Implementation'항목을 선택합니다. 그러면 pattern 문자열 매개변수에 Regex 상수값이 전달된 StringSyntax 이름의 attribute가 적용되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688293345681&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Creates a regular expression object for the specified regular expression.
/// &amp;lt;/summary&amp;gt;
public Regex([StringSyntax(StringSyntaxAttribute.Regex)] string pattern) :
this(pattern, culture: null)
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 다시 StringSyntax attribute에 mouse오른쪽 click한뒤 'Go To Implementation'항목을 선택합니다. 결과를 보면 Regex를 포함해 선택가능한 12개의 인식된 문자열 구문형식이 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688293553586&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public const string CompositeFormat = nameof(CompositeFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing date format specifiers.&amp;lt;/summary&amp;gt;
public const string DateOnlyFormat = nameof(DateOnlyFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing date and time format specifiers.&amp;lt;/summary&amp;gt;
public const string DateTimeFormat = nameof(DateTimeFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing &amp;lt;see cref=&quot;Enum&quot;/&amp;gt; format specifiers.&amp;lt;/summary&amp;gt;
public const string EnumFormat = nameof(EnumFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing &amp;lt;see cref=&quot;Guid&quot;/&amp;gt; format specifiers.&amp;lt;/summary&amp;gt;
public const string GuidFormat = nameof(GuidFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing JavaScript Object Notation (JSON).&amp;lt;/summary&amp;gt;
public const string Json = nameof(Json);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing numeric format specifiers.&amp;lt;/summary&amp;gt;
public const string NumericFormat = nameof(NumericFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing regular expressions.&amp;lt;/summary&amp;gt;
public const string Regex = nameof(Regex);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing time format specifiers.&amp;lt;/summary&amp;gt;
public const string TimeOnlyFormat = nameof(TimeOnlyFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing &amp;lt;see cref=&quot;TimeSpan&quot;/&amp;gt; format specifiers.&amp;lt;/summary&amp;gt;
public const string TimeSpanFormat = nameof(TimeSpanFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing URIs.&amp;lt;/summary&amp;gt;
public const string Uri = nameof(Uri);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing XML.&amp;lt;/summary&amp;gt;
public const string Xml = nameof(Xml);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithRegularExpressions project에서 Strings. cs라는 이름의 class file을 추가한 뒤 2개의 문자열 상수를 정의하는 문을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688293704379&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	const string digitsOnlyText = @&quot;^\d+$&quot;;
	const string commaSeparatorText = &quot;(?:^|,)(?=[^\&quot;]|(\&quot;)?)\&quot;?((?(1)[^\&quot;]*|[^,\&quot;]*))\&quot;?(?=,|$)&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio 2022에서 위 예제를 작성하면 아직까지는 정규식에 색상 구문 강조가 적용되지 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 literal string을 아래와 같이 digits-only 정규 표현식에 대한 string constant로 바꾸고&lt;/p&gt;
&lt;pre id=&quot;code_1688294148019&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex ageChecker = new(digitsOnlyText);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithText project의 comma 분리 정규표현식에서도 아래와 같이 string constant로 바꾸어 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688294497859&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex csv = new(commaSeparatorText);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Strings.cs에서는 StringSyntax attribute를 위한 namespace를 추가하고 string상수 둘다에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;StringSyntax&lt;span&gt; attribute를 적용합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1688294610163&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	[StringSyntax(StringSyntaxAttribute.Regex)]
	const string digitsOnlyText = @&quot;^\d+$&quot;;

	[StringSyntax(StringSyntaxAttribute.Regex)]
	const string commaSeparatorText = &quot;(?:^|,)(?=[^\&quot;]|(\&quot;)?)\&quot;?((?(1)[^\&quot;]*|[^,\&quot;]*))\&quot;?(?=,|$)&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 날짜의 서식화를 위한 또 다른 상수를 아래와 같이 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1688294680370&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[StringSyntax(StringSyntaxAttribute.DateTimeFormat)]
const string fullDateTime = &quot;&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fullDateTime문자열 안에 'd'를 typing 하면 IntelliSense가 작동됨을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;163&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCpSOD/btsE42Yxi7W/pwsNk5XV8PNp0slp6j1WD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCpSOD/btsE42Yxi7W/pwsNk5XV8PNp0slp6j1WD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCpSOD/btsE42Yxi7W/pwsNk5XV8PNp0slp6j1WD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCpSOD%2FbtsE42Yxi7W%2FpwsNk5XV8PNp0slp6j1WD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;163&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;163&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fullDateTime변수에 'yyyy-MM-dd'형식의 날짜 format을 지정하고 digitsOnlyText의 끝에는 \문자를 추가합니다. 이때 IntelliSense를 통해 올바른 정규표현식을 입력하는데 도움을 받을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1013&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BxXHs/btsE7uGI6kG/lDHgKKtRgzRXZkKuZAKVC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BxXHs/btsE7uGI6kG/lDHgKKtRgzRXZkKuZAKVC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BxXHs/btsE7uGI6kG/lDHgKKtRgzRXZkKuZAKVC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBxXHs%2FbtsE7uGI6kG%2FlDHgKKtRgzRXZkKuZAKVC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1013&quot; height=&quot;236&quot; data-origin-width=&quot;1013&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[StringSyntax] attribute는 .NET 7에서 새롭게 도입된 기능으로 이를 인식하는 것은 code editor의 몫입니다. .NET 7에는 이뿐만 아니라 해당 attribute에 적용할 수 있는 350개 이상의 매개변수, 속성, field를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) Source generator를 통한 정규표현식의 성능향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열값 혹은 문자열상수를 Regex의 생성자로 전달하면 class는 문자열을 분석하여 정규표현식 번역기에 의해 효휼적으로 실행될 수 있도록 최적화된 방식으로 표현식을 나타내는 내부 tree구조로 변환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규 표현식은 RegexOption을 지정함으로써 아래와 같이 compile 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688360143487&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex ageChecker = new(digitsOnlyText, RegexOptions.Compiled);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 compiling은 정규표현식의 초기생성 속도를 늦추는 부정적인 효과를 가지고 있습니다. 번역기가 실행할 tree구조가 생성되고 나면 compiler는 이 tree를 IL code로 변환하고 IL code를 native code로 JIT compile 해야 합니다. 정규표현식을 몇 번만 실행시킬 거라면 이것을 굳이 compile 할 필요가 없을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서는 [GeneratedRegex] attribute를 Regex를 반환하는 partial method에 적용하면 이를 인식하는 정규표현식에 대한 source generator를 도입하였는데 이는 정규표현식에 대한 logic을 구현하는 method의 구현을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithRegularExpressions project에서 Regexs.cs라는 새로운 class file을 생성하고 아래와 같이 partial method를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688362457867&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
    [GeneratedRegex(digitsOnlyText, RegexOptions.IgnoreCase)]
    private static partial Regex DigitsOnly();
    [GeneratedRegex(commaSeparatorText, RegexOptions.IgnoreCase)]
    private static partial Regex CommaSeparator();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 아래와 같이 digits-only 정규표현식을 반환하는 partial method를 호출하도록 생성자를 바꿔주고&lt;/p&gt;
&lt;pre id=&quot;code_1688362570923&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex ageChecker = DigitsOnly();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;comma 구분자 정규 표현식을 반환하는 partial method를 호출하도록 하는 생성자도 아래와 같이 바꿔줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688363068378&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex csv = CommaSeparator();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 각 partial method에 mouse pointer를 올려보면 표현식에 대한 동작을 설명하는 tooltip을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPkbtG/btsE5EQP4Y8/IOYuEmBWYj4q3iMuP3XSG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPkbtG/btsE5EQP4Y8/IOYuEmBWYj4q3iMuP3XSG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPkbtG/btsE5EQP4Y8/IOYuEmBWYj4q3iMuP3XSG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPkbtG%2FbtsE5EQP4Y8%2FIOYuEmBWYj4q3iMuP3XSG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;224&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DigitsOnly partial method를 mouse 오른쪽 click 하여 'Go To Definition'을 click 해 보면 자동으로 생성된 partial method의 구현체를 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLaJbw/btsE3pTYGrt/TZLMu4Nru1SNDKYWpCVW0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLaJbw/btsE3pTYGrt/TZLMu4Nru1SNDKYWpCVW0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLaJbw/btsE3pTYGrt/TZLMu4Nru1SNDKYWpCVW0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLaJbw%2FbtsE3pTYGrt%2FTZLMu4Nru1SNDKYWpCVW0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1146&quot; height=&quot;391&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 기존의 동작과 같은지를 확인합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 link에서는 .NET 7에서 정규 표현식의 구현에 대한 더 많은 내용을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot;&gt;Regular Expression Improvements in .NET 7 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1688363376173&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Regular Expression Improvements in .NET 7 - .NET Blog&quot; data-og-description=&quot;System.Text.RegularExpressions has improved significantly in .NET 7. In this post, we'll deep-dive into many of its exciting improvements.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uB2JV/hyTcAl2QIW/5ieMkckpL4trcKuUiaeJQK/img.png?width=655&amp;amp;height=223&amp;amp;face=0_0_655_223,https://scrap.kakaocdn.net/dn/Zzyym/hyTcFt64Ae/TwHMJrzehISeL5l35wefCK/img.png?width=1659&amp;amp;height=742&amp;amp;face=0_0_1659_742,https://scrap.kakaocdn.net/dn/bx4uQH/hyTcH6x7tj/LcVIVW8MoX7cLUIEslomtk/img.png?width=857&amp;amp;height=590&amp;amp;face=0_0_857_590&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uB2JV/hyTcAl2QIW/5ieMkckpL4trcKuUiaeJQK/img.png?width=655&amp;amp;height=223&amp;amp;face=0_0_655_223,https://scrap.kakaocdn.net/dn/Zzyym/hyTcFt64Ae/TwHMJrzehISeL5l35wefCK/img.png?width=1659&amp;amp;height=742&amp;amp;face=0_0_1659_742,https://scrap.kakaocdn.net/dn/bx4uQH/hyTcH6x7tj/LcVIVW8MoX7cLUIEslomtk/img.png?width=857&amp;amp;height=590&amp;amp;face=0_0_857_590');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Regular Expression Improvements in .NET 7 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;System.Text.RegularExpressions has improved significantly in .NET 7. In this post, we'll deep-dive into many of its exciting improvements.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. Collection에서 다중 개체 저장하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data와 관련된 또 다른 가장 일반적인 type으로는 collection을 들 수 있습니다. Collection은 다중값을 저장하는 일반적인 type입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection은 memory에 있는 data구조로서 모든 collection이 일부 기능을 공유하고 있기는 하지만 나름대로의 방식으로 다중 item을 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 .NET에서 collection을 사용하기 위한 가장 일반적인 type을 나타낸 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;Example type(s)&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;System.Collections&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;IEnumerable, IEnumerable&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Collection에서 사용되는 Interface와 기반 class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;System.Collections.Generic&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;List&amp;lt;T&amp;gt;, Dectionary&amp;lt;T&amp;gt;, Queue&amp;lt;T&amp;gt;, Stack&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;.NET Framework 2.0과 함께 C# 2.0에서 도입된 것으로 generic type 매개변수를 통해 원하는 type을 지정할 수 있는 collection (더 안전하고, 더 빠르며, 더 효휼적인)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;System.Collections.Concurrent&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;BlockingCollection, ConcurrentDictionary, ConcurrentQueue&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Multithread scenario에서 사용하기에 안전한 collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;System.Collections.Immutable&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;ImmutableArray, ImmutableDictionary, ImmutableList, ImmutableQueue&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;본래 collection의 content가 절대 바뀌지 않는다는 scenario를 위해 설계된 것으로 다만 새로운 instance를 생성함으로서 기존의 collection을 변경할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 모든 collection에서의 공통기능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 collection은 ICollection interface를 구현하고 있고 때문에 얼마나 많은 개체가 존재하는지를 의미하는 Count속성을 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371452448&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface ICollection : IEnumerable
{
	int Count { get; }
	bool IsSynchronized { get; }
	object SyncRoot { get; }
	void CopyTo(Array array, int index);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 한 학급의 학생에 대한 collection이 존재한다면 다음과 같이 학생의 수를 구할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371230849&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int howMany = students.Count;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 모든 collection은 IEnumerable interface를 구현합니다. 때문에 foreach문을 통해 각 요소를 열거할 수 있습니다. 그리고 이를 위해서는 IEnumerator를 구현하는 개체를 반환하는 GetEnumerator method를 가져야 하며 반환된 개체는 collection을 탐색할 수 있는 MoveNext와 Reset method 그리고 collection에서 현재 item을 가지고 있는 Current 속성을 가져야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371476056&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface IEnumerable
{
	IEnumerator GetEnumerator();
}

public interface IEnumerator
{
	object Current { get; }
	bool MoveNext();
	void Reset();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 students collection에서 각 개체에 대해 아래와 같은 동작을 수행할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371576216&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;foreach (Student s in students)
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개체 기반 collection interface뿐만 아니라 collection에 저장되는 type을 정의하는 generic type generic interface와 class도 존재합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371758609&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface ICollection&amp;lt;T&amp;gt; : IEnumerable&amp;lt;T&amp;gt;, IEnumerable
{
    int Count { get; }
    bool IsReadOnly { get; }
    void Add(T item);
    void Clear();
    bool Contains(T item);
    void CopyTo(T[] array, int index);
    bool Remove(T item);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Collection의 가용성을 지정하여 성능 향상하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 1.1부터 StringBuilder와 같은 type은 EnsureCapacity라는 이름의 method를 가지고 있어서 이를 통해 array를 저장하는 size를 미리 지정할 수 있었고 따라서 string의 마지막 size를 예상할 수 있었습니다. 이 것은 문자가 추가됨으로서 array의 size를 반복적으로 증가시키지 않아도 되므로 성능에 도움이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6부터 List&amp;lt;T&amp;gt;, Queue&amp;lt;T&amp;gt;, Stack&amp;lt;T&amp;gt;와 같은 collection은 같은 역할로 EnsureCapacity이름의 method를 가지게 되었으며 아래와 같이 사전 size를 지정할 수 있게 되었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688376054059&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; names = new();
names.EnsureCapacity(10000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Collection의 종류&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection은 사용하고자 하는 목적에 따라 lists, dictionaries, queues, sets 등 선택가능한 여러 가지가 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Lists&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IList&amp;lt;T&amp;gt;를 구현하는 List는 정렬된 collection입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688437311666&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[DefaultMember(&quot;Item&quot;)]
public interface IList&amp;lt;T&amp;gt; : ICollection&amp;lt;T&amp;gt;, IEnumerable&amp;lt;T&amp;gt;, IEnumerable
{
    T this[int index] { get; set; }
    int IndexOf(T item);
    void Insert(int index, T item);
    void RemoveAt(int index);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;DefaultMember attribute는 member의 이름이 지정되지 않았을 경우 기본적으로 접근될 member를 지정합니다. 따라서 만약 IndexOf를 기본 member로 지정하고자 한다면 [DefaultMember(&quot;IndexOf&quot;)]와 같이 사용할 수 있습니다. 예제에서 Item은 Indexer를 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IList&amp;lt;T&amp;gt;는 ICollection&amp;lt;T&amp;gt;로 부터 파생되므로 Count속성을 가지고 있으며 collection끝에 item을 추가하기 위한 Add method와 특정한 위치에 item을 추가하는 Insert method, 지정한 위치에 item을 제거하는 RemoveAt method를 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 List는 수동적으로 collection의 item 순서를 조정할 수 있습니다. List의 각 item은 자동적으로 할당된 고유한 index(또는 위치값)를 가지고 있으며 또한 T에 의해 어떠한 type으로도 item이 정의될 수 있고 필요하다면 중복으로 item을 저장시킬 수 있습니다. Index는 int type으로서 0부터 시작하므로 list의 첫 번째 item의 index값은 0이 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Index&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;apple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;banana&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;orange&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위 상태에서 banana와 orange사이에 lemon이라는 새로운 item이 삽입되면 orange의 index는 자동적으로 증가됩니다. 따라서 item의 index는 다른 item의 삽입이나 제거로 인해 바뀔 수 있음에 주의해야 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Index&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;apple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;banana&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;lemon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;orange&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;때로는 Array가 더 나은 상황임에도 굳이 List&amp;lt;T&amp;gt;나 다른 collection을 사용하는 경우가 있습니다. 그러나 이는 좋지 않은 습관입니다. Data의 size가 instance화 된 이후에 바뀌는 않는 경우라면 array를 사용하는 것이 좋습니다. 먕갸 data의 조작이 필요한 경우라면 처음에는 list를 사용하여 필요한 변경을 수행하고 그 다음 array로 변환하여 사용합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Dictionary&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dictionary는 각 값(또는 개체)이 고유한 하위 값을 가질 수 있으며 이는 collection에서 빠르게 값을 찾는 key로 사용될 수 있습니다. 여기서 key는 고유해야 합니다. 예를 들어 사람에 대한 list를 저장하는 경우라면 주민등록번호를 key로서 사용할 수 있을 것입니다. Dictionary는 Python과 Java와 같은 다른 언어에서는 hashmaps라고도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key는 실제 사전에서 index항목과 같이 생각할 수 있습니다. 단어(즉, key)는 정렬된 상태로 유지되므로 빠르게 단어의 정의를 찾을 수 있게 하며 만약 manatee단어에 대한 정의를 찾는다고 하면 사전의 중간쯤부터 시작할 수 있을 것입니다. 왜냐하면 문자 M은 alphabet의 중간쯤에 위치해 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Programming에서 Dictionary는 무엇인가를 찾을 때 위와 비슷하게 동작할 수 있습니다. Dictionary는 IDictionary&amp;lt;TKey, TValue&amp;gt; interface를 구현합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688439430168&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[DefaultMember(&quot;Item&quot;)] // aka this indexer
public interface IDictionary&amp;lt;TKey, TValue&amp;gt; : ICollection&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;, IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;, IEnumerable
{
    TValue this[TKey key] { get; set; }
    ICollection&amp;lt;TKey&amp;gt; Keys { get; }
    ICollection&amp;lt;TValue&amp;gt; Values { get; }
    void Add(TKey key, TValue value);
    bool ContainsKey(TKey key);
    bool Remove(TKey key);
    bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dictionary의 item은 값 type인 KeyValuePair&amp;lt;TKey, TValue&amp;gt; struct의 instance로서 TKey는 key의 type이며 TValue는 value의 type입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688439609425&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public readonly struct KeyValuePair&amp;lt;TKey, TValue&amp;gt;
{
    public KeyValuePair(TKey key, TValue value);
    public TKey Key { get; }
    public TValue Value { get; }
    
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void Deconstruct(out TKey key, out TValue value);
    public override string ToString();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Dictionary&amp;lt;string, Person&amp;gt;는 key로서 string을, 값(value)으로서 Person instance를 사용하는 것입니다. 아래 예제 Dictionary&amp;lt;string, string&amp;gt;은 값과 key로서 둘 다 string을 사용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;key&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;801201-1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;홍길동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;750405-2&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;홍길남&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;820712-1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;홍길석&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Stack&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack는 LIFO(last-in, first-out) 동작을 구현하기에 적합합니다. 따라서 stack에서는 전체 item을 열거할 수는 있지만 stack의 가장 상위에 있는 하나의 item에 대한 직접적인 접근 및 제거만이 가능합니다. Stack에 대한 두 번째나 세 번째 등의 접근 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack의 가장 대표적인 사례로는 word process를 들 수 있습니다. Stack을 통해 우리가 한 동작을 순서대로 기억하고 있다가 Ctrl + Z key를 누르게 되면 stack에서 마지막 동작을 읽어 되돌리게 되는 것입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Queue&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queue는 FIFO(first-in, first-out)동작을 구현하기에 적합합니다. 따라서 queue에서는 전체 item을 열거할 수는 있지만 queue의 가장 앞에 있는 item에 대한 직접적인 접근및 제거만이 가능하며 두번째나 세번째등의 접근은 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queue의 가장 대표적인 사례로는 은행에서 줄을 서 있는 사람들처럼 들어온 순서대로 작업 item을 처리할 때 queue를 사용한 경우를 들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6에서는 queue의 각 item에 position과 우선순위값이 할당된 PriorityQueue를 도입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Program.cs에 queue를 정의하고 사용하는 일반적인 방법을 나타내는 문을 아래와 같이 추가합니다. 예제는 Queue를 통해 음식점에서 손님에게 순서대로 주문한 음식을 제공하는 경우를 나타낸 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708569942672&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Queue&amp;lt;string&amp;gt; foods = new();
foods.Enqueue(&quot;제육볶음&quot;);
foods.Enqueue(&quot;된장찌개&quot;);
foods.Enqueue(&quot;김치찌개&quot;);
foods.Enqueue(&quot;백반정식&quot;);
foods.Enqueue(&quot;라면&quot;);

Output(&quot;Initial queue from front to back&quot;, foods);

string served = foods.Dequeue();
Console.WriteLine($&quot;Served: {served}.&quot;);

served = foods.Dequeue();
Console.WriteLine($&quot;Served: {served}.&quot;);
Output(&quot;Current queue from front to back&quot;, foods);

Console.WriteLine($&quot;{foods.Peek()} is next in line.&quot;);
Output(&quot;Current queue from front to back&quot;, foods);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IP2tc/btsEZoaFcvE/xd4JHOwG2nHvQQEl6Jwn6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IP2tc/btsEZoaFcvE/xd4JHOwG2nHvQQEl6Jwn6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IP2tc/btsEZoaFcvE/xd4JHOwG2nHvQQEl6Jwn6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIP2tc%2FbtsEZoaFcvE%2Fxd4JHOwG2nHvQQEl6Jwn6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;326&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Helpers.cs의 partial Program class에서 OutputPQ라는 이름의 정적 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708569942674&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputPQ&amp;lt;TElement, TPriority&amp;gt;(string title, IEnumerable&amp;lt;(TElement Element, TPriority Priority)&amp;gt; collection)
{
    Console.WriteLine(title);

    foreach ((TElement, TPriority) item in collection)
    {
        Console.WriteLine($&quot; {item.Item1}: {item.Item2}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;OutputPQ는 generic임에 주목합니다. 이를 통해 collection에 전달되는 tuple에서 사용할 2개의 type을 지정할 수 있습니다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 priority queue를 정의하고 사용하는 일반적인 방법을 나타내는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708569942675&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PriorityQueue&amp;lt;string, int&amp;gt; foods = new();
foods.Enqueue(&quot;제육볶음&quot;, 1);
foods.Enqueue(&quot;된장찌개&quot;, 4);
foods.Enqueue(&quot;김치찌개&quot;, 3);
foods.Enqueue(&quot;백반정식&quot;, 2);
foods.Enqueue(&quot;라면&quot;, 1);

OutputPQ(&quot;Initial queue from front to back&quot;, foods.UnorderedItems);
Console.WriteLine($&quot;{foods.Dequeue()} has been served.&quot;);
Console.WriteLine($&quot;{foods.Dequeue()} has been served.&quot;);

OutputPQ(&quot;Current queue :&quot;, foods.UnorderedItems);
Console.WriteLine($&quot;{foods.Dequeue()} has been served.&quot;);
            
Console.WriteLine(&quot;Adding Mark to queue with priority 2&quot;);
foods.Enqueue(&quot;오무라이스&quot;, 2);

Console.WriteLine($&quot;{foods.Peek()} will be next to be served.&quot;);
OutputPQ(&quot;Current queue :&quot;, foods.UnorderedItems);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제에서 Enqueue method의 두 번째 숫자가 우선순위에 해당합니다. 1이 가장 높은 우선순위를 4가 가장 낮은 우선순위가 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;365&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmuK90/btsE6Bzz5XW/R3R5RdtY38KColbhq8bQ4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmuK90/btsE6Bzz5XW/R3R5RdtY38KColbhq8bQ4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmuK90/btsE6Bzz5XW/R3R5RdtY38KColbhq8bQ4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmuK90%2FbtsE6Bzz5XW%2FR3R5RdtY38KColbhq8bQ4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;365&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;365&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Set&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Set은 2개의 collection사이에 집합 연산을 수행합니다. 예를 들어 과일에 대한 2개의 collection을 가지고 있을 때 2개 collection모두에서 공통적으로 속해있는(집합 사이에 교차하는) 과일의 이름을 알고 싶은 경우 set을 사용할 수 있습니다. Set에서 item은 고유해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 사용되는 Set method는 아래와 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;Add&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;Item이 존재하지 않는다면 새롭게 추가합니다. Method의 수행결과로 true라면 Item이 추가된 것이고 false라면 해당 Item이 이미 존재하는 경우입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;ExceptWith&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;매개변수로 전달된 set의 item을 set에서 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;IntersectWith&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;매개변수로 전달된 set이 아닌 item을 set에서 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;IsProperSubsetOf&lt;br /&gt;IsProperSupersetOf&lt;br /&gt;IsSubsetOf&lt;br /&gt;IsSupersetOf&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;Subset은 item이 모두 다른 set에 존재하는 set입니다. 적절한 subset은 item이 모두 다른 set에 존재하는 set이지만 다른 set에 없는 item이 최소 하나이상 있는 set입니다. Superset은 모든 item이 다른 set에 있는 set이고 적절한 superset은 다른 set의 모든 item과 다른 set에 존재하지 않는 하나이상의 item을 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;Overlaps&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;Set와 다른 set는 하나 이상의 공통 item을 공유합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;SetEquals&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;Set와 다른 set는 정확히 같은 item을 공유합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;SymmetricExceptWith&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;매개변수로 전달된 set에 속하지 않은 item을 set에서 제거하고 누락된것을 추가합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;UnionWith&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;매개변수로 전달된 set에 아직 set에 없는 item을 추가합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Set에 대한 일반적인 예제를 작성해 보기 위해 Program.cs를 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708561770243&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HashSet&amp;lt;String&amp;gt; fruits = new();

foreach(string fruit in new[] { &quot;apple&quot;, &quot;banana&quot;, &quot;orange&quot;, &quot;banana&quot; })
{
    bool result = fruits.Add(fruit);

    Console.WriteLine($&quot;result : {result}, {fruit}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biOEjj/btsE8B1QHiP/ouJbNzzX3YetE2HdbhBerk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biOEjj/btsE8B1QHiP/ouJbNzzX3YetE2HdbhBerk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biOEjj/btsE8B1QHiP/ouJbNzzX3YetE2HdbhBerk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiOEjj%2FbtsE8B1QHiP%2FouJbNzzX3YetE2HdbhBerk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;229&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Collection method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 collection은 item을 추가하거나 삭제할 때 사용하는 일련의 method가 다르며 아래 표는 collection별로 이러한 method를 정리한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;Collection&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Add method&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Remove method&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;List&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Add, Insert&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Remove, RemoveAt&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;List는 정렬되어 있으므로 item은 int형식의 index position을 갖고 있습니다. Add는 list의 끝에 새로운 item을 추가하며 Insert는 지정한 위치에 새로운 item을 추가하게 됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;Dictionary&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Add&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Remove&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;Dictionary는 정렬되어 있지 않으므로 item또한 index position을 갖고 있지 않습니다. ContainsKey method를 호출하면 해당 key가 사용되었는지 여부를 확인할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;Stack&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Push&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Pop&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;Push method는 새로운 item을 stack의 상위에 추가합니다. 따라서 첫번째 item은 맨 마지막에 있게 됩니다. Item이 Pop을 통해 제거될때는 stack의 가장 상위의 것이 제거됩니다. Peek method를 호출하면 해당 값을 삭제하지 않고도 확인할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;Queue&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Enqueue&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Dequeue&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;Enqueue method를 통해 item이 추가될때는 항상 queue의 끝에 추가됩니다. 따라서 첫번째 item은 queue의 앞에 있게 됩니다. Dequeue method를 통해 item이 제거될때는 queue의 가장 앞에 있는 item을 제거합니다. Peek method를 호출하면 해당 값을 삭제하지 않고도 확인할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) List 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy08 solution에서 WorkingWithCollections이름의 Console App project를 추가하고 Helpers.cs class를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688524462411&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
    static void Output(string title, IEnumerable&amp;lt;string&amp;gt; collection)
    {
        Console.WriteLine(title);

        foreach (string item in collection)
        {
            Console.WriteLine($&quot; {item}&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 list를 정의하고 사용하는 일반적인 방법을 나타내는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688524632676&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; cities = new();
cities.Add(&quot;Daegu&quot;);
cities.Add(&quot;Seoul&quot;);
cities.Add(&quot;Busan&quot;);

Output(&quot;Initial list&quot;, cities);
Console.WriteLine($&quot;The first city is {cities[0]}.&quot;);
Console.WriteLine($&quot;The last city is {cities[cities.Count - 1]}.&quot;);
            
cities.Insert(0, &quot;Jeju&quot;);
Output(&quot;After inserting Sydney at index 0&quot;, cities);

cities.RemoveAt(1);
cities.Remove(&quot;Seoul&quot;);
Output(&quot;After removing two cities&quot;, cities);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 cities list는 아래 2가지 다른 방식으로도 정의될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688524790235&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; cities = new() { &quot;London&quot;, &quot;Paris&quot;, &quot;Milan&quot; };&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688524812315&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; cities = new();
cities.AddRange(new[] { &quot;London&quot;, &quot;Paris&quot;, &quot;Milan&quot; });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과가 표시될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AyKnC/btsE8NlKE8c/BuT1ySNXpVEnI0VGKwrODK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AyKnC/btsE8NlKE8c/BuT1ySNXpVEnI0VGKwrODK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AyKnC/btsE8NlKE8c/BuT1ySNXpVEnI0VGKwrODK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAyKnC%2FbtsE8NlKE8c%2FBuT1ySNXpVEnI0VGKwrODK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;285&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Dictionary 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 Dictionary를 정의하고 사용하며 단어를 찾는 등의 일반적인 방법을 나타내는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688525225523&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dictionary&amp;lt;string, string&amp;gt; keywords = new();

keywords.Add(key: &quot;int&quot;, value: &quot;32-bit integer data type&quot;);
keywords.Add(&quot;long&quot;, &quot;64-bit integer data type&quot;);
keywords.Add(&quot;float&quot;, &quot;Single precision floating point number&quot;);

Output(&quot;Dictionary keys:&quot;, keywords.Keys);
Output(&quot;Dictionary values:&quot;, keywords.Values);

Console.WriteLine(&quot;Keywords and their definitions&quot;);
foreach (KeyValuePair&amp;lt;string, string&amp;gt; item in keywords)
{
    Console.WriteLine($&quot; {item.Key}: {item.Value}&quot;);
}

string key = &quot;long&quot;;
Console.WriteLine($&quot;The definition of {key} is {keywords[key]}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 첫 번째 Add method는 명명된 매개변수를 사용한 추가방식을 나타내고 있습니다. 또한 Dictionary는 아래와 같은 2가지 다른 방식을 통해 정의될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688525331275&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dictionary&amp;lt;string, string&amp;gt; keywords = new()
{
    { &quot;int&quot;, &quot;32-bit integer data type&quot; },
    { &quot;long&quot;, &quot;64-bit integer data type&quot; },
    { &quot;float&quot;, &quot;Single precision floating point number&quot; },
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688525369306&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dictionary&amp;lt;string, string&amp;gt; keywords = new()
{
    [&quot;int&quot;] = &quot;32-bit integer data type&quot;,
    [&quot;long&quot;] = &quot;64-bit integer data type&quot;,
    [&quot;float&quot;] = &quot;Single precision floating point number&quot;,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 상기예제에서 마지막 comma(,)는 생략할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cR70MY/btsE2A84tDV/8VTA4btzGEK0cb0TzoJglk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cR70MY/btsE2A84tDV/8VTA4btzGEK0cb0TzoJglk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cR70MY/btsE2A84tDV/8VTA4btzGEK0cb0TzoJglk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcR70MY%2FbtsE2A84tDV%2F8VTA4btzGEK0cb0TzoJglk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;253&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Collection 정렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;T&amp;gt; class에서는 Sort method를 호출함으로써 수동적으로 정렬할 수 있습니다(단, 각 item의 index는 바뀔 수 있습니다.). 문자열값 혹은 다른 내장 type의 list를 수동적으로 정렬하는 것은 별도의 구현이 필요 없이 작업이 가능하지만 자신만의 type으로 collection을 생성한 경우라면 해당 type은 IComparable이름의 interface를 구현해야 합니다. 이에 대한 자세한 내용은 아래 글에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 6. Interface와 Class상속&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708571031002&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 6. Interface와 Class상속&quot; data-og-description=&quot;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다. 또한 generic을 사용하여 어떻게 code를 안전하게 만들고 &quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/i1iYS/hyVqpPu2Ag/cLT1YOQk5tofRqupUcRQ91/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bQYHQR/hyVm2uQI2n/dZNmboyKfcXNBKrx50a0r0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/tWgzp/hyVqhjC3Ka/96VPTdN78p9Rrgi3VXEwn0/img.png?width=360&amp;amp;height=214&amp;amp;face=0_0_360_214&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/i1iYS/hyVqpPu2Ag/cLT1YOQk5tofRqupUcRQ91/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bQYHQR/hyVm2uQI2n/dZNmboyKfcXNBKrx50a0r0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/tWgzp/hyVqhjC3Ka/96VPTdN78p9Rrgi3VXEwn0/img.png?width=360&amp;amp;height=214&amp;amp;face=0_0_360_214');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 6. Interface와 Class상속&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다. 또한 generic을 사용하여 어떻게 code를 안전하게 만들고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack&amp;lt;T&amp;gt;또는 Queue&amp;lt;T&amp;gt; collection은 근본적인 특성상 정렬이 불가능하며 Dictionary 혹은 set에서는 정렬이 가능할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 때로는 자동적으로 정렬된 collection, 즉, item을 추가하거나 삭제하면 그것대로 순서대로 정렬된 item을 유지하는 것을 사용하는 것이 유용할 때가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 선택가능한 여러 자동정렬 collection들이 존재합니다. 이들 정렬된 collection의 차이는 미묘하지만 application에서 사용될 때 필요한 memory와 성능에는 큰 영향이 있을 수 있습니다. 따라서 필요에 따라 가장 적합한 것을 선택할 수 있도록 신중해질 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택가능한 자동정렬 collection에는 다음과 같은 것들이 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;Collection&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;SortedDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;key에 의해 정렬된 key/값 쌍의 collection입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;SortedList&amp;lt;TKey, TValue&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;key에 의해 정렬된 key/값 쌍의 collection입니다. SortedDictionary&amp;lt;TKey, TValue&amp;gt;와 비교하여 열거하는 성능은 비슷하지만 더 적은 memory를 사용하며 정렬되어 있지 않은 상태에서는 insert나 remove동작은 느리지만 정렬되어 있다면 더 빠르게 동작합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;SortedSet&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;순서대로 정렬된 상태를 유지하는 고유한 개체의 collection입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 더 전문화된 collection&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 collection 외 더 특별한 상황을 위한 다른 collection들이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Bit 값을 사용하는 compact array&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Collections.BitArray collection은 bool형식의 bit값에 대한 array를 관리하며 true라면(값이 1이라면) bit가 on이고 false라면(값이 0이라면) bit가 off임을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Efficient list&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Collections.Generics.LinkedList&amp;lt;T&amp;gt; collection은 이중연결 list를 나타내는 것으로 모든 item은 전의 item과 후의 item에 대한 참조를 갖고 있습니다. 이러한 구조로 list의 중간쯤에 자주 item에 대한 insert나 remove가 이루어지는 상황이라면 List&amp;lt;T&amp;gt;와 비교해 더 우월한 성능을 나타내며 구조의 특성상 LinkedList&amp;lt;T&amp;gt;에서 item은 memory에서 재정렬될 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Read-only , Immutable, frozen collection&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generic collection interface를 보면 IsReadOnly라는 속성이 존재함을 알 수 있습니다. 이 속성은 method에 collection을 전달할때 해당 collection에 어떠한 변경도 허용하지 말아야 할 경우 유용하게 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;T&amp;gt;와 Dictionary&amp;lt;TKey, TValue&amp;gt;와 같은 collection는 AsReadOnly method를 갖고 있는데 이를 통해 본래 collection을 참조하는 ReadOnlyCollection&amp;lt;T&amp;gt;를 생성하게 됩니다. ReadOnlyCollection&amp;lt;T&amp;gt;도 ICollection&amp;lt;T&amp;gt; interface를 구현하고 있기 때문에 Add나 Remove와 같은 method를 갖고 있지만 해당 method의 호출을 시도하면 NotImplementedException을 throw하게 됩니다. ReadOnlyCollection&amp;lt;T&amp;gt;는 참조이므로 본래 collection에서 item의 추가나 삭제와 같은 변경사항이 발생하면 당연히 ReadOnlyCollection&amp;lt;T&amp;gt;에도 그대로 변경사항이 적용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708586342751&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.OutputEncoding = System.Text.Encoding.UTF8;

void UseDictionary(IDictionary&amp;lt;string, string&amp;gt; dictionary)
{
    try
    {
        dictionary.Add(key: &quot;A7&quot;, value: &quot;홍길석&quot;); 
    }
    catch (NotSupportedException)
    {
        Console.WriteLine(&quot;collection이 읽기 전용임.(item을 추가할 수 없음).&quot;);
    }
    
    Console.WriteLine($&quot;전체 요소 수 : {dictionary.Count}&quot;);
}

Dictionary&amp;lt;string, string&amp;gt; employees = new Dictionary&amp;lt;string, string&amp;gt;();
employees.Add(&quot;A1&quot;, &quot;홍길순&quot;);
employees.Add(&quot;A2&quot;, &quot;홍길동&quot;);
employees.Add(&quot;A3&quot;, &quot;홍길영&quot;);

UseDictionary(employees);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 method에서 dicttionary type을 전달받아 새로운 item을 추가하도록 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;method에서는 IDictionary type의 인수가 정의되어 있습니다. 특정 type보다 interface를 사용하면 program에 더 많은 유연성을 부여할 수 있습니다. 예제에서도 IDictionary interface를 사용해 Dictionary&amp;lt;TKey, TValue&amp;gt;나 ReadOnlyDictionary&amp;lt;TKey, TValue&amp;gt;등 해당 interface를 구현한 모든 type을 잔달받을 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 method안에서 정상적으로 item을 추가하게 되었으므로 Count의 수는 늘어나게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn8UG0/btsE93qyyEW/SwMz5T65R1VsULHKlDZKM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn8UG0/btsE93qyyEW/SwMz5T65R1VsULHKlDZKM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn8UG0/btsE93qyyEW/SwMz5T65R1VsULHKlDZKM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn8UG0%2FbtsE93qyyEW%2FSwMz5T65R1VsULHKlDZKM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;153&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 AsReadOnly method를 통해 읽기전용 collection을 전달해 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708587060006&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UseDictionary(employees.AsReadOnly());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mAEyA/btsE98ZEHPT/7UhKK3SdYfqsNhE75dl36K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mAEyA/btsE98ZEHPT/7UhKK3SdYfqsNhE75dl36K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mAEyA/btsE98ZEHPT/7UhKK3SdYfqsNhE75dl36K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmAEyA%2FbtsE98ZEHPT%2F7UhKK3SdYfqsNhE75dl36K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;153&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method에서는 item을 추가할 수 없기 때문에 결과에서의 Count값은 기존과 동일합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Immutable collection은 말 그대로 불변의 collection을 생성합니다. 다시 말해 collection의 member는 바뀔 수 없고 따라서 추가나 삭제도 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Immutable collection은 System.Collections.Immutable namespace를 import 하게 되면 IEnumerable&amp;lt;T&amp;gt;를 구현하는 모든 collection은 여섯 개의 확장 method가 주어지는데 이를 통해 immutable list나 dictionary, hash, set 등으로 변환하는 것으로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithCollections의 Program.cs에서 System.Collections.Immutable namespace를 import 하고 cities list를 immutable list로 변환한 뒤 새로운 item을 아래와 같이 추가하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688612828537&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; cities = new();
cities.Add(&quot;Daegu&quot;);
cities.Add(&quot;Seoul&quot;);
cities.Add(&quot;Busan&quot;);

ImmutableList&amp;lt;string&amp;gt; immutableCities = cities.ToImmutableList();
ImmutableList&amp;lt;string&amp;gt; newList = immutableCities.Add(&quot;Jeju&quot;);
Output(&quot;Immutable list of cities:&quot;, immutableCities);
Output(&quot;New list of cities:&quot;, newList);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfFISq/btsEZSCAZ3l/emKiQq39VPJBDKUToea5qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfFISq/btsEZSCAZ3l/emKiQq39VPJBDKUToea5qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfFISq/btsEZSCAZ3l/emKiQq39VPJBDKUToea5qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfFISq%2FbtsEZSCAZ3l%2FemKiQq39VPJBDKUToea5qK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;191&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 cities의 immutable list는 바뀌지 않음에 주목해야 합니다. Add method를 통해 item을 추가하게 되면 자신을 바꾸는 것이 아닌 기존의 것에서 새로운 것이 추가된 상태의 list가 새롭게 반환됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;성능향상을 위해 많은 application에서는 공통적으로 접근되는 개체의 공유복사본을 cache에 복사하고 여러 thread에서의 안정적인 동작이 필요한 경우 이를 immutable로 만들거나 concurrent collection type을 사용하게 됩니다. 이와 관련해서는 아래 link에서 자세한 내용을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot;&gt;System.Collections.Concurrent Namespace | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1688613328311&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;System.Collections.Concurrent Namespace&quot; data-og-description=&quot;Provides several thread-safe collection classes that should be used in place of the corresponding types in the System.Collections and System.Collections.Generic namespaces whenever multiple threads are accessing the collection concurrently. However, access&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bBU9HE/hyTeahupd0/ekCX75UVI8D3ZCS7H6yxwk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bBU9HE/hyTeahupd0/ekCX75UVI8D3ZCS7H6yxwk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;System.Collections.Concurrent Namespace&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Provides several thread-safe collection classes that should be used in place of the corresponding types in the System.Collections and System.Collections.Generic namespaces whenever multiple threads are accessing the collection concurrently. However, access&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generic collection은 설계된 방식으로 인해 잠재먹인 성능 issue를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째로는 dictionary에서 key와 값에 사용되는 type이 어떤 것이냐에 따라 성능에 큰 영향을 준다는 것입니다. 거의 모든 type을 다 받아들일 수 있기 때문에 사실상 성능을 최적화하게에는 무리가 있었던 것입니다. 실세 여기에 사용되는 type은 string과 int가 가장 흔한데 만약 .NET team이 type을 이것으로만 한정하게 된다면 아마도 최적화가 가능했을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째로 collection은 동적입니다. 이는 collection에 새로운 item이 언제든지 추가될 수 있고, 또 언제든지 제거될 수 있다는 것을 의미합니다. Collection이 더이상 변경되지 않는다는것을 보장할 수 있다면 이 역시 최적화가 가능한 부분이기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 .NET 8에서는 frozen collection이라는 새로운 개념을 도입하게 되었습니다.&amp;nbsp;현재 frozen collection은 향후에 더 추가될 수 있지만 FrozenDictionary&amp;lt;TKey, TValue&amp;gt;와 FrozenSet&amp;lt;T&amp;gt;가 존재합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708589391317&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections.Frozen;

Console.OutputEncoding = System.Text.Encoding.UTF8;

Dictionary&amp;lt;string, string&amp;gt; employees = new Dictionary&amp;lt;string, string&amp;gt;();
employees.Add(&quot;A1&quot;, &quot;홍길순&quot;);
employees.Add(&quot;A2&quot;, &quot;홍길동&quot;);
employees.Add(&quot;A3&quot;, &quot;홍길영&quot;);

FrozenDictionary&amp;lt;string, string&amp;gt; frozenEmployee = employees.ToFrozenDictionary();

foreach(var item in frozenEmployee)
{
    Console.WriteLine(item.Key + &quot;:&quot; + item.Value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 기존 employees dictionary를 frozen dictionary로 변환한 뒤 각 item을 표시하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 내용에 따라 Add method의 동작은 type에 따라 달라진다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List&amp;lt;T&amp;gt; : 새로운 item은 list의 끝에 추가합니다.&lt;/li&gt;
&lt;li&gt;Dictionary&amp;lt;TKey, TValue&amp;gt; : 새로운 item은 내부구조에 따라 결정된 위치에 추가합니다.&lt;/li&gt;
&lt;li&gt;ReadOnlyCollection&amp;lt;T&amp;gt; : item추가가 지원되지 않으며 추가를 시도하는 경우 예외를 발생시키게 됩니다.&lt;/li&gt;
&lt;li&gt;ImmutableList&amp;lt;T&amp;gt; : 새로 추가된 item을 포함한 새로운 list를 반환합니다. 본래 list에는 영향을 주지 않습니다.&lt;/li&gt;
&lt;li&gt;ImmutableDictionary&amp;lt;TKey, TValue&amp;gt; : 새로 추가된 item을 포함한 새로운 list를 반환합니다. 본래 list에는 영향을 주지 않습니다.&lt;/li&gt;
&lt;li&gt;FrozenDictionary&amp;lt;TKey, TValue&amp;gt; : 해당 method가 존재하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;frozen collection에 관한 더 많은 정보는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot;&gt;System.Collections.Frozen Namespace | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708589969840&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;System.Collections.Frozen Namespace&quot; data-og-description=&quot;Explore all classes and interfaces of the System.Collections.Frozen namespace.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zbUan/hyVmQnKgrT/CAFpMDnkt9PTNmJwzEZ0vk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zbUan/hyVmQnKgrT/CAFpMDnkt9PTNmJwzEZ0vk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;System.Collections.Frozen Namespace&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Explore all classes and interfaces of the System.Collections.Frozen namespace.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) Collection 표현식을 사용한 초기화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 11이전에는 collection, array, span등을 초기화 할때 int형인 경우 다음과 같은 방식을 사용했었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708645814464&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] myArray = { 1, 2, 3 };
List&amp;lt;int&amp;gt; MyList = new() { 1, 2, 3 };
Span&amp;lt;int&amp;gt; MyySpan = stackalloc int[] { 1, 2, 3 };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 C# 12부터는 collection, array, span등을 초기화 할 수 있는 새로운 문법을 도입하게 되어 대괄호를 사용한 일관성있는 초기화가 가능해쪘습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708645976885&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] myArray = [ 1, 2, 3 ];
List&amp;lt;int&amp;gt; MyList =  [ 1, 2, 3 ];
Span&amp;lt;int&amp;gt; MyySpan = [ 1, 2, 3 ];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) Collection에 대한 모범사례&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection을 처리해야 하는 method를 만들어야 한다고 가정해 보면 예를 들어 최대한의 유용성을 활용하기 위해 입력 매개변수를 IEnumerable&amp;lt;T&amp;gt;로 선언하여 method를 generic으로 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688630836377&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void ProcessCollection&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; collection)
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 여기에 int나 string 또는 사용자 type 등 IEnumerable&amp;lt;T&amp;gt;를 구현하는 다른 모든 type을 포함하는 list, queue, stack과 같은 array를 전달할 수 있습니다. 그러나 해당 method로 모든 collection을 전달하고자 하는 이런 유연성은 꽤 많은 처리비용을 요구할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IEnumerable&amp;lt;T&amp;gt;로 하면 이에 대한 성능저하의 문제점으로 개체를 heap에 할당해야 하는 것으로 기인합니다. 이러한 memory할당을 피하려면 위의 method에서 아래와 같이 구체적인 type을 명시해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688631970800&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void ProcessCollection&amp;lt;T&amp;gt;(List&amp;lt;T&amp;gt; collection)
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 IEnumerator&amp;lt;T&amp;gt;에서 참조 type을 반환하는 GetEnumerator method대신 구조체를 반환하는 List&amp;lt;T&amp;gt;의 GetEnumerator method를 사용하게 됩니다. 이것만으로 2~3배 정도의 성능상향을 기대할 수 있으며 그만큼 memory로 더 적게 사용합니다. 성능과 관련된 다른 모든 권장사항들이 그렇듯이 실제 code를 test 함으로써 성능에 관한 이점을 확인해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. Span, Index 그리고 range&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Core 2.1에서 Microsoft의 목표 중 하나는 성능 및 resource의 사용을 개선하는 것이었으며 이에 대한 핵심 .NET 기능이 Span&amp;lt;T&amp;gt; type입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Span을 통한 memory 효율성 증가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array를 다룰 때 어떤 경우에는 기존의 것에서 일부만 따로 떼어낸 새로운 복사본이 필요할 때가 있습니다. 그런데 실제 이와 같은 복사본을 생성하는 것은 memory안에 동일한 같은 개체를 만들게 되므로 그리 효휼적이라고 할 수는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 만약 기존 array에서 전체 혹은 부분적으로 동일한 array를 대신 사용해야 한다면 본래 array에 대한 거울과 같은 span을 사용할 수 있습니다. Memory사용율과 성능면에서 더 효율적인 span은 오로지 array에 대해서만 사용할 수 있으며 memory가 연속되어야 하므로 collection에서는 사용할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Index type을 통한 위치 식별&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 8.0에서는 array안에서 item의 위치와 2개의 index를 사용해 item의 범위를 식별하기 위한 2가지 기능을 도입했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;list의 개체는 indexer에 int값을 전달함으로써 다음과 같이 접근할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688696667467&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int index = 3;
Person p = people[index];
char letter = name[index];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Index type은 아래와 같이 사용할 수 있으며 position을 식별하기 위한 공식적인 방법이고 또한 아래와 같이 끝에서부터 계산할 수 있는 fromEnd를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688696933593&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Index i1 = new(value: 3);
Index i2 = 3;

Index i3 = new(value: 5, fromEnd: true);
Index i4 = ^5;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index를 정의하기 위해서 예제와 같이 명시적으로 숫자를 사용하거나 new(value: 3)과 같이 사용할 수 있습니다. 이때 index는 시작부터 3번째를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝에서 부터 counting 되도록 하는 경우 fromEnd를 사용하거나 ^(caret) 문자를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Range type으로 범위 식별하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Range type은 범위의 시작과 끝을 나타내기 위해 Index값을 사용하며 생성자 혹은 정적 method를 사용해 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688697271408&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Range r1 = new(start: new Index(3), end: new Index(7));
Range r2 = new(start: 3, end: 7);
Range r3 = 3..7;
Range r4 = Range.StartAt(3);
Range r5 = 3..;
Range r6 = Range.EndAt(3); 
Range r7 = ..3;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 range type은 위 예제에서 첫 번째와 두 번째같이 생성자를 통해 생성할 수 있습니다. 세 번째는 C# 8부터 사용가능한 방식이며 네번째는 정적 method를 사용한 것으로 세번째 index부터 끝까지를 의미합니다. 다섯번째는 네번째와 의미가 같고 여섯번째는 끝에서 부터 세번째 index를 의미합니다. 일곱 번째는 여섯 번째와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장 method는 문자열값(내부적으로 char 배열을 사용), int array 그리고 span에 추가되어 범위를 더 쉽게 다룰 수 있습니다. 이들 확장 method는 매개변수로 범위를 수용하고 Span&amp;lt;T&amp;gt;를 반환함으로써 memory를 더 효휼적으로 사용할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Indexe, range,&amp;nbsp; 그리고 span사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Span을 반환하는 index와 range는 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy08 solution에서 WorkingWithRanges이름의 Console app project를 생성하고 Program.cs에서 기존의 문을 모두 삭제한 뒤 string type의 substring method에서 범위를 지정하여 이름을 추출하고 비교하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688712901189&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string name = &quot;Gildong hong&quot;;

//성과 이름의 길이를 확인합니다.
int lengthOfFirst = name.IndexOf(' ');
int lengthOfLast = name.Length - lengthOfFirst - 1;

//성과 이름을 분리해 가져옵니다.
string firstName = name.Substring(startIndex: 0, length: lengthOfFirst);
string lastName = name.Substring(startIndex: name.Length - lengthOfLast, length: lengthOfLast);

Console.WriteLine($&quot;First name: {firstName}, Last name: {lastName}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 span을 사용해 구현하면 다음과 같이 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688712937221&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ReadOnlySpan&amp;lt;char&amp;gt; nameAsSpan = name.AsSpan();
ReadOnlySpan&amp;lt;char&amp;gt; firstNameSpan = nameAsSpan[0..lengthOfFirst];
ReadOnlySpan&amp;lt;char&amp;gt; lastNameSpan = nameAsSpan[^lengthOfLast..^0];

Console.WriteLine(&quot;First name: {0}, Last name: {1}&quot;, arg0: firstNameSpan.ToString(), arg1: lastNameSpan.ToString());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 결과를 다음과 같을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf8JpE/btsE40sT9mC/rcDMhNdfi2uZIjXByD6wH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf8JpE/btsE40sT9mC/rcDMhNdfi2uZIjXByD6wH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf8JpE/btsE40sT9mC/rcDMhNdfi2uZIjXByD6wH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf8JpE%2FbtsE40sT9mC%2FrcDMhNdfi2uZIjXByD6wH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;82&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. Network Resource&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부와의 통신이 필요한 application이라면 network와 관련된 여러 가지 작업이 필요합니다. 아래 표는 그런 경우 사용가능한 type을 나열한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 225px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 17px;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 17px;&quot;&gt;Type&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 40px;&quot;&gt;System.Net&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 40px;&quot;&gt;Dns, Uri, Cookie, WebClient, IPAddress&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 40px;&quot;&gt;DNS server, URIs, IP address관련및 기타&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 34px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 34px;&quot;&gt;System.Net&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 34px;&quot;&gt;FtpStatusCode, FtpWebRequest, FtpWebResponse&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 34px;&quot;&gt;FTP server 관련&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 40px;&quot;&gt;System.Net&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 40px;&quot;&gt;HttpStatusCode, HttpWebRequest, HttpWebResponse&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 40px;&quot;&gt;HTTP server와 관련된 것으로서 website와 service등을 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 40px;&quot;&gt;System.Net.Http&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 40px;&quot;&gt;HttpClient, HttpMethod, HttpRequestMessage, HttpResponseMessage&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 40px;&quot;&gt;HTTP server와 관련된 것으로서 website와 service등을 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 34px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 34px;&quot;&gt;System.Net.Mail&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 34px;&quot;&gt;Attachment, MailAddress, MailMessage, SmtpClient&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 34px;&quot;&gt;Mail을 발송하는 SMTP server관련&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 20px;&quot;&gt;System.Net .NetworkInformation&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 20px;&quot;&gt;IPStatus, NetworkChange, Ping, TcpStatistics&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;저수준 network protocol관련&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) URI, DNS, IP 주소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network resource를 사용하는 예를 작성하기 위해 csStudy08 solution에서 WorkingWithNetworkResources이름의 Console App을 생성하고 Program.cs에서 기존의 문을 모두 삭제한 뒤 아래의 namespace를 import 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688714368403&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Net;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 사용자에게 website주소의 입력을 요청하고 주소가 입력되면 Uri type을 사용해 해당 주소를 HTTP, FTP 등의 scheme, port 번호, host 등 각각의 요소로 분해하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688714626124&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.Write(&quot;Enter a valid web address (or press Enter): &quot;);

string? url = Console.ReadLine();
if (string.IsNullOrWhiteSpace(url))
{
    url = &quot;http://cliel.com/&quot;;
}

Uri uri = new(url);
Console.WriteLine($&quot;URL: {url}&quot;);
Console.WriteLine($&quot;Scheme: {uri.Scheme}&quot;);
Console.WriteLine($&quot;Port: {uri.Port}&quot;);
Console.WriteLine($&quot;Host: {uri.Host}&quot;);
Console.WriteLine($&quot;Path: {uri.AbsolutePath}&quot;);
Console.WriteLine($&quot;Query: {uri.Query}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 정확한 website주소를 입력하게 되면 아래와 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XgJBM/btsE8AmpTZQ/RZ1ZsJtiMXEMrpg2K1V3P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XgJBM/btsE8AmpTZQ/RZ1ZsJtiMXEMrpg2K1V3P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XgJBM/btsE8AmpTZQ/RZ1ZsJtiMXEMrpg2K1V3P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXgJBM%2FbtsE8AmpTZQ%2FRZ1ZsJtiMXEMrpg2K1V3P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;170&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 입력된 website주소에 대한 ip를 확인할 수 있는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688714761755&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IPHostEntry entry = Dns.GetHostEntry(uri.Host);
Console.WriteLine($&quot;{entry.HostName} has the following IP addresses:&quot;);
foreach (IPAddress address in entry.AddressList)
{
    Console.WriteLine($&quot; {address} ({address.AddressFamily})&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 정확한 website주소를 입력하면 다음의 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIdR0I/btsEZbWNVLS/eUzqFKHnhVFlWJmKaKW5P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIdR0I/btsEZbWNVLS/eUzqFKHnhVFlWJmKaKW5P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIdR0I/btsEZbWNVLS/eUzqFKHnhVFlWJmKaKW5P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIdR0I%2FbtsEZbWNVLS%2FeUzqFKHnhVFlWJmKaKW5P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;205&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;205&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Server로 ping 보내기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 입력된 website주소의 web server 상태는 ping응답을 받음으로써 확인할 수 있습니다. Program.cs에서 이를 구현하기 위한 code를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688715147723&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;Host: {uri.Host}&quot;);

Ping ping = new();
Console.WriteLine(&quot;Pinging server. Please wait...&quot;);

PingReply reply = ping.Send(uri.Host);
Console.WriteLine($&quot;{uri.Host} was pinged and replied: {reply.Status}.&quot;);

if (reply.Status == IPStatus.Success)
{
    Console.WriteLine(&quot;Reply from {0} took {1:N0}ms&quot;, arg0: reply.Address, arg1: reply.RoundtripTime);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 정확한 website주소를 입력해 보면 다음의 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6ea3s/btsE3pGuQ3O/jfRpXQPzxJGwty0hlOH121/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6ea3s/btsE3pGuQ3O/jfRpXQPzxJGwty0hlOH121/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6ea3s/btsE3pGuQ3O/jfRpXQPzxJGwty0hlOH121/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6ea3s%2FbtsE3pGuQ3O%2FjfRpXQPzxJGwty0hlOH121%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;173&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>array</category>
      <category>c#</category>
      <category>Collection</category>
      <category>index</category>
      <category>number</category>
      <category>pattern matching</category>
      <category>Regular Expression</category>
      <category>span</category>
      <category>TEXT</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/598</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-8-%EA%B3%B5%EC%9A%A9-NET-Type#entry598comment</comments>
      <pubDate>Fri, 23 Feb 2024 12:09:55 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 7. .NET Packaging과 배포</title>
      <link>https://cliel.tistory.com/entry/XFile</link>
      <description>&lt;div id=&quot;queryBox&quot; style=&quot;color: #333333; text-align: start;&quot; data-tiara-layer=&quot;query-box&quot;&gt;
&lt;div id=&quot;queryButtons&quot; style=&quot;background-color: #ffffff;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000; background-color: #fafbfc; letter-spacing: 0px;&quot;&gt;C# 키워드가 .NET 유형과 어떻게 관련되어 있는지, 네임스페이스와 어셈블리 간에는 어떤 관계가 있는지에 대한 것을 알면 C#언어를 이해하는데 도움이 될 수 있습니다. 또한 .NET library에서 이전 .NET framework library를 어떻게 사용하고 이식할 수 있는지의 여부도 함께 알아볼 것이며 이를 통해 .NET을 좀더 폭넓게 활용할 수 있을 것입니다.&lt;/span&gt;&lt;span style=&quot;color: #000000; background-color: #fafbfc; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;1. .NET 8&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000; background-color: #fafbfc; letter-spacing: 0px;&quot;&gt;.NET에서는 Base Class Library (BCL) API를 통해 수 많은 기능들을 제공하고 있습니다. .NET Standard를 통해서는 다른 전체 .NET platform간 이런 기능들을 재사용할 수 있도록 하고 있는데 때문에 &lt;/span&gt;&lt;span style=&quot;color: #000000; background-color: #fafbfc; letter-spacing: 0px;&quot;&gt;지금의 .NET과 이전의 것을 적절히 이해해둘 필요가 있습니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;.NET Standard 2.0의 최소규격을 지원하는 .NET Core 2.0부터는 .NET Core의 첫번째 version에는 없던 많은 API를 제공하고 있으므로 여기서 부터가 중요한 전환점이 될 수 있습니다. .NET Framework 개발자가 최신의 개발과 관련하여 15년간 이용할 수 있었던 library와 application은 지금 .NET과 통합되었고 Windows는 물론 macOS와 다양한 Linux에서 동작할 수 있습니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;.NET Standard 2.1은 3,000여개의 새로운 API가 추가되었는데 이들 중 몇몇은 이전 version과의 호환성을 깨는 runtime의 변경이 필요했습니다. 따라서 .NET Framework 4.8은 .&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;NET Standard 2.0만을 구현하며 .NET Core 3.0, Xamarin, Mono, 그리고 Unity는 .NET Standard 2.1을 구현하게 되었습니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;.NET 5에 들어서는 project에서 .NET 5만을 사용한다면 .NET Standard의 필요성을 제거할 수 있게 되었고 이는 .NET 6, .NET 7에서도 동일합니다. 하지만 여전히 .NET Framework project와 Xamarin mobile app을 생성해야 하는 경우도 있으므로 지금도 .NET Standard 2.0과 2.1 class library를 생성할 수 있습니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;현재 .NET 6와 .NET 7은 자체 .NET MAUI를 통해 완벽하게 mobile과 desktop app을 지원하고 있는데 이로서 .NET Standard의 필요성은 점차 감소하고 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;아래는 지난 5년동안의 .NET의 진행상황을 요약하기 위해 주요 .NET Core와 최신 .NET을 동등한 .NET Framework와 비교한 것입니다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.NET Core 1.x : 2016년 3월이 현재 version이며 .NET Framework 4.6.1에 비해 훨씬 작은 API를 가지고 있습니다.&lt;/li&gt;
&lt;li&gt;.NET Core 2.x : .NET Framework 4.7.1과 동일하게 .NET Standard 2.0을 구현함으로서 동등한 API에 도달하였습니다.&lt;/li&gt;
&lt;li&gt;.NET Core 3.x :&amp;nbsp; .NET Framework 4.8에서 구현하지 않은 .NET Standard 2.1을 구현함으로서 월씬 더 많은 API를 지원하게 되었습니다.&lt;/li&gt;
&lt;li&gt;.NET 5 : 상당히 향상된 성능과 함께 더 .NET Framework 4.8에 비해 더 많은 API를 지원합니다.&lt;/li&gt;
&lt;li&gt;.NET 6 : 지속적인 성능 향상과 API확장을 이루었으며 2022년 5월에 추가된 .NET MAUI를 통해 mobile app을 지원할 수 있게 되었습니다.&lt;/li&gt;
&lt;li&gt;.NET 7 : .NET MAUI로 mobile app지원을 위한 최종 통합 version입니다.&lt;/li&gt;
&lt;li&gt;.NET 8 : 지속적인 성능 향상이 이루어지고 있으며 모든 신규 project에 적용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) .NET Core 1.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2016년 6월에 release 되었으며 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;ASP.NET Core를 사용하는 Linux용&amp;nbsp;&lt;/span&gt;web과 cloud application 그리고 service를 포함한 최신의 cross-platform app개발을 위해 안정적인 API를 제공하는데 초점이 맞춰진 version입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) .NET Core 1.1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2016년 11월에 release 되었으며 bug를 수정하고 지원되는 Linux의 배포한 수를 증가되었으며 .NET Standard 1.6를 지원하게 되었습니다. 또한 성능의 증가도 이루어졌는데 특히 web app과 service를 위한 ASP.NET Core에서 큰 성능향상이 이루어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) .NET Core 2.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2017년 8월에 release되었으며 .NET Standard 2.0을 구현하였고 .NET Framework library를 참조하고 더 많은 성능향상을 이루었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) .NET Core 2.1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2018년 5월에 release 되었으며 확장가능한 tooling system(사용 목적에 따라 각각의 도구(기능)를 선택/조합하는 체계)에 중점을 두었습니다. Span&amp;lt;T&amp;gt;, 암호화 및 압축 그리고 예전 windows application을 이식하는데 필요한 20,000가지의 추가적인 API와 함께 Windows Compatibility Pack, Entity Framework Core값 변환, LINQ GroupBy 변환, data seeding, query type과 같은 새로운 type이 추가되었으며 아래 표의 기능을 포함해 전반적인 성능 향상이 이루어졌습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 77px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Spans&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Brotli compression&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Brotli algorithm을 통한 압축방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;EF Core Lazy loading&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;lazy(지연) loading 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;EF Core Data seeding&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;-&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) .NET Core 2.2&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2018년 12월에 release 되었으며 runtime에서의 진단향상, 선택적 계층 compile등에 중점을 두었으며 ASP.NET Core과 Entity Framework에 NetTopologySuite (NTS) library, query tag, 소유 entity collection 등의 type을 사용하는 공간 data지원등 새로운 기능이 추가되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) .NET Core 3.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2019년 9월에 release 되었으며 Windows forms, WPF(Windows Presentation Foundation) 그리고 Entity Framework 6.3, side-by-side와 app-local 개발, fast JSON reader, serial port 접근, IoT(Internet of Things) solution을 위한 pinout 접근등을 사용하는 desktop application구축하는데 필요한 지원에 중점을 두었습니다. 또한 아래 기능을 포함해 기본 계층 compile이 추가되었습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Embedding .NET in-app&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Index and Range&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Text.Json&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;고성능 JSON 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) .NET Core 3.1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2019년 12월에 release 되었으며 bug수정과 개선에 초점이 맞춰져 Long Term Support (LTS)가 되었고 이로서 2022년 12월까지 지원이 이루어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) .NET 5.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2020년 10월에 release 되었으며 mobile을 제외한 다양한 .NET platform을 통합하는데 주력하였습니다. platform을 개선하고 아래 표의 기능을 포함해 전반적으로 성능향상이 이루어졌습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Half type&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #161616; text-align: start;&quot;&gt;반정밀도 부동 소수점 숫자&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;정규 표현식 성능 향상&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;정규 표현식 성능 향상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Text.Json 향상&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;고성능 JSON 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;EF Core generated SQL&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;EF Core Filtered Include&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Entity filtering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;EF Core의 Humanizer 통합&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) .NET 6.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년 10월에 release 되었으며 data관리를 위한 EF Core로 더 많은 기능과 함께 시간과 날짜에 관한 새로운 type이 추가되었습니다. 또한 아래 표의 기능을 포함해 전반적인 성능향상이 이루어졌습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 177px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;.NET SDK 상태 확인&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;.NET SDK update 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Apple Silicon 지원&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;배포가능한 console application 생성가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Link trim mode 기본 사용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;app trimming을 통한 app size 감소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;List&amp;lt;T&amp;gt;에 대한 EnsureCapacity&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;collection용량 확보를 통한 성능 향상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;RandomAccess를 사용하는 저수준 file API&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;EF Core 구성 규칙&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;새로운 LINQ method&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;TryGetNonEnumeratedCount&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) .NET 7.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 10월에 release 되었으며 mobile platform을 통합하고 string syntax coloring과 IntelliSense 같은 새로운 기능이 추가되었습니다. tar 형식의 압축을 생성하거나 풀어낼 수 있게 되었고 EF Core를 통한 insert와 upate성능이 향상되었습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;[StringSyntax] attribute&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;regular expression syntax coloring 활성화&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;[GeneratedRegex] attribute&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;source generator를 통한 정규 표현식 성능 향상&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Tar archive support&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;tar 압축 file&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;ExecuteUpdate와 ExecuteDelete&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;더욱 효과적인 update및 삭제&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Order와 OrderDescending&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;item 자체 정렬&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(11) .NET 8.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 [&amp;quot;orderedList&amp;quot;,{&amp;quot;columnSpan&amp;quot;:&amp;quot;none&amp;quot;,&amp;quot;order&amp;quot;:1},&amp;quot;listItem&amp;quot;,{&amp;quot;closed&amp;quot;:false,&amp;quot;nested&amp;quot;:false}]&quot; data-ke-size=&quot;size16&quot;&gt;C# 12 및 F# 8 언어의 새로운 기능과 개선 사항을 포함하고 있으며 Dynamic Profile-Guided Optimization (PGO)이라는 새로운 코드 생성기를 도입하여 실제 사용에 기반한 코드 최적화를 이룰 수 있습니다. 실제 이를 통해 통해 애플리케이션 성능을 최대 20%까지 향상시킬 수 있습니다. 이 밖에 AI사용을 위한 기능이 강화되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(12) .NET 5 이후의 성능 향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 지난 몇년간 .NET에 대한 상당한 성능향상을 이루어 왔습니다. 이와 관련된 상세한 내용은 아래 글을 통해 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot;&gt;Performance Improvements in .NET 5 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687070290757&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Performance Improvements in .NET 5 - .NET Blog&quot; data-og-description=&quot;Explore many performance improvements, big and small, that have gone into the .NET 5 runtime and core libraries to make apps and services leaner and faster.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dclyKU/hyS2F1FAfc/ypbhCUOibp9KMm9KbpukLk/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dclyKU/hyS2F1FAfc/ypbhCUOibp9KMm9KbpukLk/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Performance Improvements in .NET 5 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Explore many performance improvements, big and small, that have gone into the .NET 5 runtime and core libraries to make apps and services leaner and faster.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot;&gt;Performance Improvements in .NET 6 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687070308473&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Performance Improvements in .NET 6 - .NET Blog&quot; data-og-description=&quot;Take a rip-roarin' tour through hundreds of PRs worth of performance improvements for .NET 6.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Vl1uy/hyS1pMVtQE/l7Nr2q6AuKFOX6V1zgx2t0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Vl1uy/hyS1pMVtQE/l7Nr2q6AuKFOX6V1zgx2t0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Performance Improvements in .NET 6 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Take a rip-roarin' tour through hundreds of PRs worth of performance improvements for .NET 6.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot;&gt;Performance Improvements in .NET 7 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687070318805&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Performance Improvements in .NET 7 - .NET Blog&quot; data-og-description=&quot;.NET 7 is fast. Really fast. This post deep-dives into hundreds of performance improvements that contributed to that reality.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btz7Pt/hyS1deEGnm/dx8GV5jXIZYvkx1Ojm88mk/img.png?width=315&amp;amp;height=288&amp;amp;face=0_0_315_288&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btz7Pt/hyS1deEGnm/dx8GV5jXIZYvkx1Ojm88mk/img.png?width=315&amp;amp;height=288&amp;amp;face=0_0_315_288');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Performance Improvements in .NET 7 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET 7 is fast. Really fast. This post deep-dives into hundreds of performance improvements that contributed to that reality.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot;&gt;Performance Improvements in .NET 8 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1707631183304&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Performance Improvements in .NET 8 - .NET Blog&quot; data-og-description=&quot;.NET 7 was super fast, .NET 8 is faster. Take an in-depth tour through over 500 pull requests that make that a reality.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cMKeXV/hyVf9VC1aL/w2nlsoFfueg2iYrQAxlGz1/img.png?width=1207&amp;amp;height=703&amp;amp;face=0_0_1207_703,https://scrap.kakaocdn.net/dn/3CLaY/hyVf7Dwh4m/RprK3k1yRXaxANesjiZfm0/img.png?width=1225&amp;amp;height=493&amp;amp;face=0_0_1225_493,https://scrap.kakaocdn.net/dn/ctRL9A/hyVfYfvqrM/xr4VQvcKn4jwFh5hS7aIJK/img.png?width=1396&amp;amp;height=415&amp;amp;face=0_0_1396_415&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cMKeXV/hyVf9VC1aL/w2nlsoFfueg2iYrQAxlGz1/img.png?width=1207&amp;amp;height=703&amp;amp;face=0_0_1207_703,https://scrap.kakaocdn.net/dn/3CLaY/hyVf7Dwh4m/RprK3k1yRXaxANesjiZfm0/img.png?width=1225&amp;amp;height=493&amp;amp;face=0_0_1225_493,https://scrap.kakaocdn.net/dn/ctRL9A/hyVfYfvqrM/xr4VQvcKn4jwFh5hS7aIJK/img.png?width=1396&amp;amp;height=415&amp;amp;face=0_0_1396_415');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Performance Improvements in .NET 8 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET 7 was super fast, .NET 8 is faster. Take an in-depth tour through over 500 pull requests that make that a reality.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(13) Update를 위한 .NET SDK 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6에서 부터 Microsoft는 설치 SDK와 runtime의 version을 확인하기 위한 명령을 추가했고 이를 통해 update가 필요하다고 확인되는 경우 이를 알려줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령은 아래와 같으며&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;sdk&amp;nbsp;check&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 경우처럼 update가 필요한 항목을 확인할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;.NET&amp;nbsp;SDKs: &lt;br /&gt;Version&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status &lt;br /&gt;------------------------ &lt;br /&gt;8.0.101&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;&lt;br /&gt;.NET&amp;nbsp;Runtimes: &lt;br /&gt;Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Version&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status &lt;br /&gt;------------------------------------------------------------------------------------- &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;5.0.17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;5.0&amp;nbsp;is&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.11&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.11&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.12&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.12&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.13&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.13&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.AspNetCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.14&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.AspNetCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.26&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.26&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.26&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.AspNetCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.AspNetCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8.0.1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8.0.1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8.0.1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The&amp;nbsp;latest&amp;nbsp;versions&amp;nbsp;of&amp;nbsp;.NET&amp;nbsp;can&amp;nbsp;be&amp;nbsp;installed&amp;nbsp;from&amp;nbsp;&lt;a href=&quot;https://aka.ms/dotnet-core-download.&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://aka.ms/dotnet-core-download.&lt;/a&gt;&amp;nbsp;For&amp;nbsp;more&amp;nbsp;information&amp;nbsp;about&amp;nbsp;.NET&amp;nbsp;lifecycles,&amp;nbsp;see&amp;nbsp;&lt;a href=&quot;https://aka.ms/dotnet-core-support.&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://aka.ms/dotnet-core-support.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2 .NET Component&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 아래와 같은 요소로 이루어져 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Language compiler : C#이나 F#등으로 된 source code를 assembly로 저장된 IL code로 변환시킵니다. C# 6.0 이후부터는 compiler를 Roslyn이라는 이름으로 open-source화 하였습니다.&lt;/li&gt;
&lt;li&gt;Common Language Runtime(CoreCLR) : Assembly를 load 하여 해당 assembly에 저장된 IL code를 해당 computer의 CPU에 맞는 native code로 compile한뒤 thread와 memory 같은 관리 resource환경 안에서 code를 실행하게 됩니다.&lt;/li&gt;
&lt;li&gt;Base Class Libraries(BCL 또는 CoreFX) : Application을 구축할 때 공통적인 동작을 수행하기 위해 Nuget을 통하여 package 및 배포된 type의 사전 build 된 assembly를 포함할 수 있습니다. 이를 사용하여 LEGO 부품을 조립하듯 필요한 것을 빠르고 안정적으로 구축할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Assembly, NuGet package, namespace&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assembly는 type이 filesystem으로 저장된 것입니다. Code를 배포하기 위한 mechanism인데 예를 들어 System.Data.dll assembly는 data를 관리하기 위한 type을 포함하고 있습니다. 다른 assembly의 다른 type을 사용하기 위해서는 해당 assembly가 참조되어 있아야 하며 이때 assembly는 정적(pre-created) 또는 동적(generated at runtime)인 것이 될 수 있습니다. 또한 assembly는 class library인 dll이나 console app과 같은 exe의 단일 file로 compile 될 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assembly는 NuGet package를 통해 내려받기 가능하도록 배포될 수 있으며 이때 다수의 assembly나 다른 resource를 포함할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft NuGet feed는 아래 주소에서 찾을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.nuget.org/&quot;&gt;NuGet Gallery | Home&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687135794081&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;NuGet Gallery | Home&quot; data-og-description=&quot;The NuGet Gallery is the central package repository for NuGet, the package manager for .NET.&quot; data-og-host=&quot;www.nuget.org&quot; data-og-source-url=&quot;https://www.nuget.org/&quot; data-og-url=&quot;https://www.nuget.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OSDC2/hyS2DwshbC/ksSW2uVsYNx6oYKd2OU0u0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://www.nuget.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.nuget.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OSDC2/hyS2DwshbC/ksSW2uVsYNx6oYKd2OU0u0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;NuGet Gallery | Home&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The NuGet Gallery is the central package repository for NuGet, the package manager for .NET.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.nuget.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Namespace&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace는 type의 주소입니다. 단순한 이름대신 주소체계를 사용함으로써 type을 식별하기 위한 mechanism으로 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 가지 예로 .NET에서 System.Web.Mvc namespace의 interface인 IActionFilter는 System.Web.Http.Filters namespace의 IActionFilter에 비해 namespace가 다르므로 다른 interface로 구별될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Assembly 의존성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assembly가 class library로 compile 되고 다른 assembly에서의 사용을 위해 이를 제공하게 된다면 이때 file은 .dll 확장자를 가지게 되며 독립적으로 실행될 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로, assembly가 application으로 compile 되면 이때는 exe확장자를 가지게 되며 독립적으로 실행가능한 상태가 됩니다. (.NET Core 3.0이전에 console app은 dll file로 compile 되었으며 이때는 dotnet run 명령을 통해 실행될 수 있었습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 assembly는 하나 또는 그 이상의 class library를 의존성으로 참조할 수 있습니다. 하지만 소위 순환참조는 불가능하기 때문에 예를 들어 B라는 assembly는 A라는 assembly에서 이미 B assembly를 참조하고 있다면 A assembly를 참조할 수 없습니다. 때문에 compiler는 순환참조를 유발할 수 있는 의존성 참조를 추가하려고 한다면 경고를 표시하게 됩니다. 대게는 오래전에 작성된 code에서 순환참조에 대한 오류가 나오기 쉬우며 만약 정말 순환참조가 필요한 상황이 온다면 이는 interface로 관련 문제를 해결하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Microsoft .NET Project SDK&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 console application은 Microsoft .NET project SDK에 대한 의존성 참조를 가지고 있습니다. 이 platform은 NuGet package에서 거의 대부분의 application에서 필요로 하는, 예를 들어 System.Int32나 System.tring type과 같은 수천 개의 type들을 포함하고 있으며&amp;nbsp;.NET을 사용할 때는 project file에서 필요로 하는 의존성 assembly와 NuGet package 그리고 platform 등을 참조하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Assembly에서 namespace와 type&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 일반적인 .NET type은 System.Runtime.dll assembly에 있으며 assembly와 namespace는 항상 1:1로 mapping 되지는 않습니다. 단일 assembly라도 다수의 namespace를 포함할 수 있으며 namespace는 다수의 assembly에서 정의될 수 있습니다. 아래 표는 type을 지원하기 위해 사용되는 일부 assembly와 namespace 간 연관성을 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Assembly&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;사용 Namespace&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;제공 Type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Runtime.dll&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System,&lt;br /&gt;System.Collections,&lt;br /&gt;System.Collections.Generic&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Int32, String, IEnumerable&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Console.dll&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Threading.dll&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Threading&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Interlocked,&lt;br /&gt;Monitor,&lt;br /&gt;Mutex&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Xml.XDocument.dll&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Xml.Linq&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;XDocument,&lt;br /&gt;XElement,&lt;br /&gt;XNode&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) NuGet package&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 하나가 아닌 몇 개의 package별로 나누어졌고 이를 NuGet이라는 package 관리 기술을 사용해 게시하였습니다. 이렇게 게시된 각각의 package들은 같은 이름의 단일 assembly로 표현됩니다. 예를 들어 System.Collections package는 System.Collections.dll assembly를 포함합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Package는 다음과 같은 장점 및 특징을 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공개 feed(&lt;span style=&quot;background-color: #ffffff; color: #161616; text-align: start;&quot;&gt;패키지를 인터넷의 모든 사용자와 공개적으로 공유하는 데 사용)&lt;/span&gt;에 쉽게 게시할 수 있습니다.&lt;/li&gt;
&lt;li&gt;재사용이 가능합니다.&lt;/li&gt;
&lt;li&gt;자체 일정에 따라 게시될 수 있습니다.&lt;/li&gt;
&lt;li&gt;다른 package와 독립적으로 test 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;같은 assembly를 다른 OS와 CPU에 따라 build 한 여러 version을 포함시킴으로써 다른 OS와 CPU를 지원할 수 있습니다.&lt;/li&gt;
&lt;li&gt;하나의 library에 대해서만 특정한 종속성을 가질 수 있습니다.&lt;/li&gt;
&lt;li&gt;참조되지 않은 package는 배포의 일부가 아니므로 app은 더 작아질 수 있습니다. 아래 표는 가장 중요한 package와 해당 package에서 중요한 type의 일부를 나타낸 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Package&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;중요 type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Runtime&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Object, String, Int32, Array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Collections&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;List&amp;lt;T&amp;gt;, Dictionary&amp;lt;TKey, TValue&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Net.Http&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;HttpClient, HttpResponseMessage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.IO.FileSystem&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;File, Directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Reflection&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Assembly, TypeInfo, MethodInfo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Framework&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Package는 API를 정의하며 framework는 이런 package를 group화 합니다. 어떠한 package도 없는 framework는 어떠한 API도 정의하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET package는 각각 일련의 framework를 지원합니다. 예를 들어 System.IO.FileSystem package version 4.3.0은 아래 framework를 지원합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.NET Standard, version 1.3 이후&lt;/li&gt;
&lt;li&gt;.NET Framework, version 4.6 이후&lt;/li&gt;
&lt;li&gt;Six Mono와 Xamarin platform (Xamarin.iOS 1.0)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 상세한 내용은 아래 주소를 참조하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.nuget.org/packages/System.IO.FileSystem/&quot;&gt;NuGet Gallery | System.IO.FileSystem 4.3.0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687154065350&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;nugetgallery:package&quot; data-og-title=&quot;System.IO.FileSystem 4.3.0&quot; data-og-description=&quot;Provides types that allow reading and writing to files and types that provide basic file and directory support. Commonly Used Types: System.IO.FileStream System.IO.FileInfo System.IO.DirectoryInfo System.IO.FileSystemInfo System.IO.File System.IO.Directory&quot; data-og-host=&quot;www.nuget.org&quot; data-og-source-url=&quot;https://www.nuget.org/packages/System.IO.FileSystem/&quot; data-og-url=&quot;https://nuget.org/packages/System.IO.FileSystem/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xfePT/hyS2xJ8EP8/285bmFhmuXskEGoCFVh2dK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://www.nuget.org/packages/System.IO.FileSystem/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.nuget.org/packages/System.IO.FileSystem/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xfePT/hyS2xJ8EP8/285bmFhmuXskEGoCFVh2dK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;System.IO.FileSystem 4.3.0&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Provides types that allow reading and writing to files and types that provide basic file and directory support. Commonly Used Types: System.IO.FileStream System.IO.FileInfo System.IO.DirectoryInfo System.IO.FileSystemInfo System.IO.File System.IO.Directory&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.nuget.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Type 사용을 위한 namespace importing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 namespace가 assembly와 type에 어떻게 관련되는지를 알아보도록 하겠습니다. csStudy07 이름의 빈 solution을 생성하고 그 안에 'AssembliesAndNamespaces'이름의 project를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AssembliesAndNamespaces의 Program.cs에서 기존 구문을 모두 삭제하고 아래와 같이 XDocument type의 instance를 생성하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687160824080&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;XDocument doc = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 Project를 build 하면 다음과 같은 compiler error message가 발생하게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;The&amp;nbsp;type&amp;nbsp;or&amp;nbsp;namespace&amp;nbsp;name&amp;nbsp;'XDocument'&amp;nbsp;could&amp;nbsp;not&amp;nbsp;be&amp;nbsp;found&amp;nbsp;(are&amp;nbsp;you&amp;nbsp;missing&amp;nbsp;a&amp;nbsp;using&amp;nbsp;directive&amp;nbsp;or&amp;nbsp;an&amp;nbsp;assembly&amp;nbsp;reference?)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;XDocument type은 우리가 compiler에게 해당 type의 namespace가 무엇인지를 알려주지 않았기 때문에 인식될 수 없습니다. 설령 project가 type을 포함하고 있는 assembly를 참조하고 있다고 하더라도 type이름에 namespace에 대한 접두사를 붙이거나 해당 namespace를 import 해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XDocument class의 이름에 mouse를 대면 하단에 풍선도움말이 표시되는데 이때 'Show potential pixes'를 click한뒤 menu에 나오는 System.Xml.Linq를 선택하면 자동적으로 해당 Namespace가 file의 상단에 using문을 추가함으로써 import 될 것입니다. 일단 namespace가 import 되고 나면 namespace에 속하는 모든 type은 code에서 namespace전체를 붙여줄 필요 없이 이름만 입력하는 것으로 사용가능하게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio를 사용하는 것이라면 위에서 처럼 type을 입력하는 것만으로 인식가능한 namespace를 자동으로 import 해줄 것입니다.(2022 version부터)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) C# keyword와 .NET type의 연결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서는 문자열을 다루는 keyword가 2가지가 있는데 하나는 string이고 다른 하나는 String입니다. 이 둘의 차이는 소문자 s와 대문자 S 뿐이며 사실상 용도와 동작에 있어서는 아무런 차이가 없습니다. int와 string과 같은 C# keyword는 class library assembly에 있는 .NET Type의 별칭입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 string keyword를 사용하는 경우에 compiler는 이를 System.String type으로 인식하게 되고 int를 사용하는 경우에는 System.Int32 type으로 인식합니다. 문자열을 다루기 위해 String이라고 하는 것보다는 string으로 사용하는 것이 더 익숙하고 편리한 면이 있으며 Int32보다는 int로 사용하는 것도 같은 이유기 때문에 좀 더 편리한 code의 작성을 위해 별칭으로 따로 만들어둔 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 문자열값을 가진 2개의 변수를 선언하는데 하나는 string으로 다른 하나는 String으로 선언합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687230304876&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s1 = &quot;Hello&quot;;
String s2 = &quot;World&quot;;
Console.WriteLine($&quot;{s1} {s2}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 두 변수는 정확히 동일하게 취급되어 동일한 처리를 하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project의 csproj file에서 전역적으로 import 되는 System namespace를 사용하지 않도록 하는 요소를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687230571979&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
	&amp;lt;Using Remove=&quot;System&quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래와 같이 compiler 오류가 발생할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfFAdI/btsEEFi6O2C/po5qYELMTiydwDuyDE2NsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfFAdI/btsEEFi6O2C/po5qYELMTiydwDuyDE2NsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfFAdI/btsEEFi6O2C/po5qYELMTiydwDuyDE2NsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfFAdI%2FbtsEEFi6O2C%2Fpo5qYELMTiydwDuyDE2NsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;99&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 String은 System namespace의 String type을 사용한다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 String과 같은 type대신 C# keyword를 사용하면 위에서와 같이 해당 type의 namespace import를 신경 쓰지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● C# 별칭과 .NET type의 Mapping&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 18가지 C# type keyword와 같이 연결된 .NET Type을 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Keyword&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;.NET type&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;Keyword&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;.NET type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.String&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;char&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Char&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;sbyte&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.SByte&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;byte&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;short&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Int16&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;ushort&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.UInt16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;int&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Int32&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;uint&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.UInt32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;long&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Int64&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;ulong&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.UInt64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;nint&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.IntPtr&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;nuint&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.UIntPtr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;float&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Single&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;double&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Double&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;decimal&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Decimal&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;bool&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;object&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Object&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;dynamic&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Dynamic.DynamicObject&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 .NET Programming 언어 compiler도 같은 방식을 사용할 수 있습니다. 예를 들어 VB.NET의 경우에는 Integer라는 이름의 type을 가지고 있으며 이는 System.Int32의 별칭으로 연결됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● native-sized integers&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 9에서는 native-sized integers에 대한 별칭에 해당하는 nint와 nuint를 도입하였습니다. 여기서 저장되는 integer값의 크기는 platform에 따라 다릅니다. 따라서 32bit process에서는 32bit integer로 저장되며 sizeof()에서는 4byte를 반환하고 64bit process에서는 64bit integer로, sizeof()에서는 8byte를 반환합니다. 해당 별칭의 이름이 IntPtr/UIntPtr인데 이를 통해 해당 별칭은 integer값이 저장된 memory의 위치를 나타낸다는 것을 알 수 있으며 실제 저장되는 type은 process에 따라 System.Int32 또는 System.Int64가 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687241682365&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;int.MaxValue = {int.MaxValue:N0}&quot;);
Console.WriteLine($&quot;nint.MaxValue = {nint.MaxValue:N0}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvL0M8/btsEEEYQFjE/z2QavkqdIKWnmKzWJXMS3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvL0M8/btsEEEYQFjE/z2QavkqdIKWnmKzWJXMS3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvL0M8/btsEEEYQFjE/z2QavkqdIKWnmKzWJXMS3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvL0M8%2FbtsEEEYQFjE%2Fz2QavkqdIKWnmKzWJXMS3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;88&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Type의 위치 표시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서 아래와 같이 string이나 String의 변수를 생성하고&lt;/p&gt;
&lt;pre id=&quot;code_1687242455189&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 type을 선택한 뒤 mouse오른쪽 button을 눌러 'Go to Definition'을 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddpHvK/btsEEDZUF7y/ktLM2IUvK6YUD0Yu9hrrAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddpHvK/btsEEDZUF7y/ktLM2IUvK6YUD0Yu9hrrAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddpHvK/btsEEDZUF7y/ktLM2IUvK6YUD0Yu9hrrAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddpHvK%2FbtsEEDZUF7y%2FktLM2IUvK6YUD0Yu9hrrAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1258&quot; height=&quot;218&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code editor를 보면 assembly file명은 'System.Runtime.dll'이지만 class는 System namespace에 있음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 지난 글에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 2. C#&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Runtime.dll에 포함된 type은 0 임을 언급한 바 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Runtime.dll assembly가 포함하고 있는 것은 type-forwarder입니다. 이들은 assembly안에 포함되어 있는 것처럼 표시하지만 다른 곳에 구현되어 있는 특별한 type입니다. 이 경우 string은 고도로 최적화된 code를 사용해 .NET runtime내부에 깊이 구현되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) .NET Standard를 사용한 legacy platform과의 code공유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Standard 이전에는 PCL(Portable Class Libraries)가 있었습니다. PCL에서는 code의 library를 만들고 명시적으로 library에서 지원할 Xamarin, Silverlight, Windows 등의 platform을 지정할 수 있었고, 그렇게 하면 library는 지정된 platform에 의해서 지원되는 API와의 상호작용에 사용될 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Microsoft는 이것이 지속가능하리라고 판단하지 않고 미래 모든 .NET platform에서 지원할 단일 API인 .NET Standard를 생성하게 됩니다. .NET Standard에 이어 .NET Standard 2.0에서는 중요한 최신 .NET platform의 통합을 시도했고 2019년 말에 발표된 .NET Standard 2.1에서는 .NET Core 3.0과 그해 Xamarin에서만이 새로운 기능을 지원했습니다. (앞으로 언급되는 .NET Standard는 .NET Standard 2.0을 의미합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Standard는 HTML5와 같이 둘다 platform이 지원할 수 있는 표준에 해당합니다. Google의 Chrome browser나 Microsoft의 Edge browser가 HTML5의 표준을 구현하듯 .NET Core, .NET Framework 그리고 Xamarin은 모두 .NET Standard를 구현합니다. 만약 다양한 legacy .NET에 걸쳐 작동하는 type의 library를 만들고자 한다면 .NET Standard를 통해 가장 쉽게 구현할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET Standard 2.1에서 많은 API가 추가되어 runtime의 변화가 필요했으며 .NET Framework는 가능한 한 변화 없이 유지되어야 할 Microsoft의 legacy platform이므로 .NET Framework 4.8에서는 .NET Standard 2.1을 구현하기 보다는 .NET Standard 2.0에서 남아있습니다. 만약 .NET Framework에서 지원되어야 할 필요가 있다면 비록 최신의 version이 아니고 모든 최근의 언어와 BCL의 새로운 기능을 지원하지 않지만 .NET Standard 2.0상의 class library를 만들 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 .NET Standard version을 목표로 할지에 대한 선택에서는 지원하고자 하는 platform과 가능한 기능사이에 균형을 맞추는 것이 중요합니다. 하위 version은 더 많은 platform을 지원하지만 더 작은 API를 갖게 됩니다. 상위 version은 적은 platform을 지원하지만 더 많은 API를 가질 수 있습니다. 일반적으로는 필요로 하는 모든 API를 지원하는 선에서 하위 version을 선택할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) 다른 SDK를 사용하는 class library의 기본 framework&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class library를 만들기 위해 dotnet SDK tool을 사용할 때 어떤 framework가 기본적으로 사용될지 알고 있을 필요가 있습니다. 아래 표는 이를 간략히 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;SDK&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;새로운 class library에 사용될 기본 target framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;netstandard2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;.NET 6&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;net6.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;.NET 7&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;net7.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;.NET 8&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;net8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 class library가 기본적으로 특정 .NET의 version을 대상으로 한다 하더라도 이것이 기본 template을 사용해 class library project를 생성한 이후에는 바꿀 수 없다는 것을 의미하는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정에 의해 target framework를 library참조가 필요한 project에서 지원하는 값으로 바꿀 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;Class library target framework&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;project에서 사용할 수 있는 대상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;netstandard2.0&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET Framework 4.6.1이후, .NET Core 2.0이후, .NET 5.0이후, Mono 5.4이후, Xamarin.Android 8.0이후, Xamarin.iOS 10.14이후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;netstandard2.1&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET Core 3.0이후, .NET 5.0이후, Mono 6.4이후, Xamarin.Android 10이후, Xamarin.iOS 12.16이후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;net6.0&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET 6.0이후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;net7.0&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET 7.0이후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;net8.0&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET 8.0이후&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class library의 대상 framework를 확인하고 필요하다면 더 적절한 것으로 바꿀 수 있습니다. 기본값을 그대로 수용하기보다는 무엇이 적합한지 의식적으로 결정을 내리시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) .NET Standard 2.0 class library를 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Standard 2.0을 사용해 모든 .NET legacy platform과 Windows 및 macOS, Linux OS의 cross-platform에서 사용가능하며 다양한 .NET API에 access 할 수 있는 class library를 만들어볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy07 solution에 SharedLibrary이름의 Class Library유형의 project를 추가합니다. 이때 .NET Standard 2.0을 target으로 설정해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOcIMe/btsEINzG8hk/MxHf8Lppn9Uy8d7yJqtC70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOcIMe/btsEINzG8hk/MxHf8Lppn9Uy8d7yJqtC70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOcIMe/btsEINzG8hk/MxHf8Lppn9Uy8d7yJqtC70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOcIMe%2FbtsEINzG8hk%2FMxHf8Lppn9Uy8d7yJqtC70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1430&quot; height=&quot;784&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;만약 .NET Standard 2.0 기능만 사용하는 type뿐만 아니라 .NET 7의 새로운 기능을 사용하는 type을 생성해야 한다면 이 두 개의 class library를 분리하여 생성해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 class library를 생성하는 대신 multi-targeting을 지원하는 방법도 존재합니다. 자세한 내용은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting&quot;&gt;Cross-platform targeting for .NET libraries | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687329848302&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Cross-platform targeting for .NET libraries&quot; data-og-description=&quot;Best practice recommendations for creating cross-platform .NET libraries.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/l6XqB/hyS4yJacmX/0tiHO2RdY6zLQYVfKSGuQ0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300,https://scrap.kakaocdn.net/dn/b5iX3v/hyS4p6wJNh/tc7e6GyqVnOdhw2yWdgYj1/img.png?width=1581&amp;amp;height=853&amp;amp;face=0_0_1581_853&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/l6XqB/hyS4yJacmX/0tiHO2RdY6zLQYVfKSGuQ0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300,https://scrap.kakaocdn.net/dn/b5iX3v/hyS4p6wJNh/tc7e6GyqVnOdhw2yWdgYj1/img.png?width=1581&amp;amp;height=853&amp;amp;face=0_0_1581_853');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Cross-platform targeting for .NET libraries&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Best practice recommendations for creating cross-platform .NET libraries.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(11) .NET SDK 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 dotnet 명령줄 실행에서는 system에 설치된 가장 최신의 .NET SDK를 사용합니다. 그러나 때로는 이렇게 사용되는 SDK를 변경해야 하는 경우가 있습니다. 그리고 이것은 global.json file을 사용함으로써 가능한데 dotnet명령은 &lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;global.json file을 &lt;/span&gt;현재 folder와 상위 folder에거 검색합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금부터 설명하는 절차를 굳이 따라 할 필요는 없지만 그래도 해보고자 한다면 .NET 6.0 SDK를 설치해야 하며 아래 link로부터 가져올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot;&gt;Download .NET 6.0 (Linux, macOS, and Windows) (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687349761147&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Download .NET 6.0 (Linux, macOS, and Windows)&quot; data-og-description=&quot;.NET 6.0 downloads for Linux, macOS, and Windows. .NET is a free, cross-platform, open-source developer platform for building many different types of applications.&quot; data-og-host=&quot;dotnet.microsoft.com&quot; data-og-source-url=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot; data-og-url=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ExuXQ/hyS4q5LWe2/uq3JJNlMiKuWZNQnIf3hS1/img.png?width=238&amp;amp;height=238&amp;amp;face=0_0_238_238,https://scrap.kakaocdn.net/dn/stNqx/hyS4xcLtPP/6kkwkXuInXP2B2AUCyc5Q1/img.png?width=459&amp;amp;height=238&amp;amp;face=0_0_459_238&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ExuXQ/hyS4q5LWe2/uq3JJNlMiKuWZNQnIf3hS1/img.png?width=238&amp;amp;height=238&amp;amp;face=0_0_238_238,https://scrap.kakaocdn.net/dn/stNqx/hyS4xcLtPP/6kkwkXuInXP2B2AUCyc5Q1/img.png?width=459&amp;amp;height=238&amp;amp;face=0_0_459_238');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Download .NET 6.0 (Linux, macOS, and Windows)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET 6.0 downloads for Linux, macOS, and Windows. .NET is a free, cross-platform, open-source developer platform for building many different types of applications.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dotnet.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy07 project folder하위에 ControlSDK이름의 새로운 folder를 만들고 해당 folder안에서 terminal을 열어 아래 명령을 내려줍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet --list-sdks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 .NET SDK 6.0을 설치했다면 아마도 아래와 같은 결과를 표시할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;6.0.418&amp;nbsp;[C:\Program&amp;nbsp;Files\dotnet\sdk] &lt;br /&gt;8.0.101&amp;nbsp;[C:\Program&amp;nbsp;Files\dotnet\sdk]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 아래의 명령을 내려 global.json이름의 file을 만들고 아래와 같이 설정하여 설치한 .NET Core 6.0 SDK의 사용을 강제하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet new globaljson --sdk-version 6.0.410&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 file을 열어보면 아마도 다음과 같이 저장되어 있을 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687350201338&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;sdk&quot;: {
    &quot;version&quot;: &quot;6.0.410&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 아래 명령을 통해 class library project의 생성을 시도합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet new classlib&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 정상적으로 처리된다면 해당 project가 생성되어 있을 테지만 .NET SDK 6.0이 설치되지 않은 상태라면 다음의 오류가 표시될 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;The&amp;nbsp;command&amp;nbsp;could&amp;nbsp;not&amp;nbsp;be&amp;nbsp;loaded,&amp;nbsp;possibly&amp;nbsp;because: &lt;br /&gt;&amp;nbsp;&amp;nbsp;*&amp;nbsp;You&amp;nbsp;intended&amp;nbsp;to&amp;nbsp;execute&amp;nbsp;a&amp;nbsp;.NET&amp;nbsp;application: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;The&amp;nbsp;application&amp;nbsp;'new'&amp;nbsp;does&amp;nbsp;not&amp;nbsp;exist. &lt;br /&gt;&amp;nbsp;&amp;nbsp;*&amp;nbsp;You&amp;nbsp;intended&amp;nbsp;to&amp;nbsp;execute&amp;nbsp;a&amp;nbsp;.NET&amp;nbsp;SDK&amp;nbsp;command: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;A&amp;nbsp;compatible&amp;nbsp;.NET&amp;nbsp;SDK&amp;nbsp;was&amp;nbsp;not&amp;nbsp;found. &lt;br /&gt;&lt;br /&gt;Requested SDK version: 6.0.410&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(12) SDK와 framework의 혼합사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대개의 경우 신규 project를 생성할때 Microsoft의 장기적(3년)지원을 받기 위해 long-term version의 .NET을 사용할 것입니다. 이후에 새로운 .NET version이 나오고 거기에 맞춰 upgrade할 수도 있겠지만 그렇게 하지 않는다고 해서 사용중인 .NET runtime의 생명주기동안 C#언어의 개선된 기능이나 성능에 대한 이점을 포기할 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;향후에도 C# compiler를 설치하고 사용하면서 기존 .NET에 대한 사용을 지속할 수 있습니다. 이걸 시기적으로 보면 다음과 같이 설명할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2023년 11월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;.NET SDK 8.0을 설치하고 이를 사용해 .NET 8로 target이 설정된 project를 build합니다. 이때 C# 12가 기본적으로 사용됩니다. 이후에는 매달 개발자 computer에서&amp;nbsp; .NET 8 SDK와 사용자 computer상에서 .NET 8 runtime update patch가 이루어집니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2024년 2월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;새로운 C# 언어와 .NET library기능을 확인해 보기 위해 선택적으로 .NET SDK 9 Preview를 설치할 수 있습니다. .NET 8로 target이 맞춰진 경우에는 새로운 library기능은 사용할 수 없습니다. Preview는 매년 2월 ~ 10월 사이에 release되며 공식 blog의 공지사항을 통해 Preview에서의 새로운 기능을 확인할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2024년 11월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;.NET SDK 9.0.x를 설치하고 이를 사용해 .NET 8로 target이 설정된 project를 build합니다. 이때 C# 13이 기본적으로 사용되며 언어의 새로운 기능역시 사용할 수 있습니다. 또한 SDK와 runtime에 대한 완전한 지원을 받을 수 있고 .NET 8 target project에서도 EF Core 9의 새로운 기능을 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2025년 2월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;새로운 C# 언어와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;.NET library기능을 확인해 보기 위해 선택적으로 .NET SDK 10 Preview를 설치할 수 있습니다. 만약 .NET 8 project를 새로운 version으로 전환해야 한다면 .NET9와 .NET10의 새로운 library및 ASP.NET Core 기능을 적용할 수 있는지에 대한 계획을 세워야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2025년 11월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; .NET SDK 10.0.x를 설치하고 이를 사용해 .NET 8로 target이 설정된 project를 build합니다. 이때 C# 14이 기본적으로 사용되며 언어의 새로운 기능역시 사용할 수 있습니다. 다만 .NET 8은 LTS이므로 .NET 8의 수명주기종료가 되는 2026년 11월까지 .NET 10으로의 전환이 필요합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DK4mc/btsES2wO4M2/I77YejXiKdhAvBhbXoPWH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DK4mc/btsES2wO4M2/I77YejXiKdhAvBhbXoPWH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DK4mc/btsES2wO4M2/I77YejXiKdhAvBhbXoPWH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDK4mc%2FbtsES2wO4M2%2FI77YejXiKdhAvBhbXoPWH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;254&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET SDK를 설치할때는 모든 .NET project를 build하기 위해 기본적으로 가장 최신의 version이 사용됨을 기억하시기 바랍니다. 따라서 만약 현 시점에서&amp;nbsp; .NET 9 SDK preview가 설치되면 인위적으로 사용할 version을 강제하지 않는 이상 project build를 위해 해당 version이 사용될 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 배포를 위한 code 게시하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 소설과 같은 글을 쓴 경우라면 이것을 다른 사람이 읽도록 하기 위해서는 '출판'해야 할 것입니다. 대부분의 개발자는 자신의 project에서 사용하거나 또는 app을 실행하는 일반사용자를 위한 code를 작성합니다. 그렇게 하기 위해서는 class library를 packaging 하거나 실행 가능한 형태의 application으로 게시를 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET application을 배포하고 게시하기 위해서는 아래 3가지 방법을 사용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Framework-dependent deployment (FDD)&lt;/li&gt;
&lt;li&gt;Framework-dependent executable (FDE)&lt;/li&gt;
&lt;li&gt;Self-contained&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 application 및 여기에 해당하는 package종속성을 같이 배포하기로 했지만 .NET자체는 제외한다면 대상 computer에 설치된 .NET에 의존해야 합니다. 이것은 server에 web application을 배포하기에 적합한 방식입니다. 왜냐하면 .NET은 물론 다른 web application 이미 server에 존재할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Framework-dependent deployment (FDD)는 dotnet 명령줄 도구를 통해 실행될 수 있는 DLL을 배포하는 경우를 의미하며 Framework-dependent executables (FDE)는 직접적으로 실행될 수 있는 EXE를 배포하는 경우를 의미합니다. 이 둘은 모두 system에 적절한 version의 .NET runtime이 설치되어 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 다른 사람에게 USB memory를 통해 application을 전달하고자 할 수 있습니다. 이런 경우 전달받은 사용자의 computer에서 실행될 수 있도록 self-contained deployment를 수행할 수 있습니다. 물론 이렇게 하는 경우 배포 file의 size가 커질 수 있지만 관련된 모든 library를 포함하게 되므로 실행하는 데는 아무런 문제가 없을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 게시를 위한 console app 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy07 solution에서 DotNetEverywhere이름의 Console App project를 생성합니다. 그리고 Program.cs에서 기존 구문을 모두 삭제하고 console app이 어디서든 작동할 수 있음을 말하는 message와 현재 운영체제에 대한 정보를 출력하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687416918001&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;I can run everywhere!&quot;);
Console.WriteLine($&quot;OS Version is {Environment.OSVersion}.&quot;);

if (OperatingSystem.IsMacOS())
{
    Console.WriteLine(&quot;I am macOS.&quot;);
}
else if (OperatingSystem.IsWindowsVersionAtLeast(major: 10, build: 22000))
{
    Console.WriteLine(&quot;I am Windows 11.&quot;);
}
else if (OperatingSystem.IsWindowsVersionAtLeast(major: 10))
{
    Console.WriteLine(&quot;I am Windows 10.&quot;);
}
else
{
    Console.WriteLine(&quot;I am some other mysterious OS.&quot;);
}

Console.WriteLine(&quot;Press ENTER to stop me.&quot;);
Console.ReadLine();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.(실행되는 환경에 따라 약간씩 다를 수 있습니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ebWOdG/btsEGTf3wn2/nK7SRRYHQ1uYyxNHnx4aY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ebWOdG/btsEGTf3wn2/nK7SRRYHQ1uYyxNHnx4aY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ebWOdG/btsEGTf3wn2/nK7SRRYHQ1uYyxNHnx4aY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebWOdG%2FbtsEGTf3wn2%2FnK7SRRYHQ1uYyxNHnx4aY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;107&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project file(csproj)을 열어 &amp;lt;PropertyGroup&amp;gt; 요소 안에 세 가지 운영체제를 대상으로 하기 위한 runtime 식별자를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687417120952&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&amp;lt;RuntimeIdentifiers&amp;gt;
	win10-x64;osx-x64;osx.11.0-arm64;linux-x64;linux-arm64
&amp;lt;/RuntimeIdentifiers&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가한 요소는 다음과 같은 의미를 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;win10-x64 : Windows 10 또는 Windows Server 2016 64-bit를 의미합니다. 만약 Microsoft Surface Pro X, Surface Pro 9(SQ 3) 또는 Windows Dev Kit 2023으로 배포하고자 한다면 win10-arm64로 설정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;osx-64 : macOS Sierra 10.12이상을 의미합니다. 또한 osx.10.15-x64 (Catalina), osx.13.0-x64 (Ventura on Intel), osx.13.0-arm64 (Ventura on Apple Silicon)등 특정 version을 명시할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;linux-x64 : Ubuntu, CentOS, Debian, Fedora와 같은 Linux의 대부분의 desktop 배포판을 의미합니다. linux-arm을 사용하는 경우에는 Raspbian 또는 Raspberry Pi OS 32-bit를 의미하며 linux-arm64라면 Raspberry Pi running Ubuntu 64-bit를 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;특정 runtime을 식별하기 위해 사용가능한 2가지 요소가 있습니다. &amp;lt;RuntimeIdentifier&amp;gt;는 특정한 한 가지만을 지정해야 하는 경우에 사용하며 &amp;lt;RuntimeIdentifiers&amp;gt;는 예제와 같이 여러 가지를 지정해야 하는 경우에 사용합니다. 그런데 만약 잘못된 값을 사용하게 된다면 compiler는 error를 발생시키게 되는데 경우에 따라 단 하나의 잘못된 문자로 error가 발생하는 경우에 무엇이 잘못되었는지 찾기가 어려워질 수 있으므로 주의해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) dotnet 명령&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET SDK를 설치하게 되면 dotnet이라는 이름의 command line interface(CLI)를 같이 설치하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 새로운 project 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET CLI는 template을 사용해 새로운 project를 현재 folder에 생성하는 명령을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terminal을 열고 .NET 7이상 이라면 'dotnet new list'를, .NET 6라면 'dotnet new --list'혹은 'dotnet new -l'명령을 내려줍니다. 그러면 다음과 같이 현재 설치된 template을 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pITaj/btsEINBQZ8r/uOKaNqsTGeKu2MXLZkQRV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pITaj/btsEINBQZ8r/uOKaNqsTGeKu2MXLZkQRV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pITaj/btsEINBQZ8r/uOKaNqsTGeKu2MXLZkQRV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpITaj%2FbtsEINBQZ8r%2FuOKaNqsTGeKu2MXLZkQRV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;1426&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 dotnet 명령의 switche는 길고 짧은 version을 각각 사용할 수 있기 때문에 만약 --list라 하면 -l처럼 사용할 수 있습니다. 짧은 것은 typing 하기에 빠를 수 있지만 다른 사람이나 심지어 본인도 잘못 명령을 해설할 수 있으므로 긴 것을 사용하면 의미를 좀 더 명확히 전달할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET MAUI는 Linux를 지원하지 않습니다. 해당 작업은 open source community에서 계속 진행되겠지만 진정한 cross-platform UI를 사용해야 한다면 Avalonia를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://avaloniaui.net/&quot;&gt;Avalonia UI - Home&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1707889180258&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Avalonia UI - Cross-Platform UI Framework for .NET&quot; data-og-description=&quot;Pixel-Perfect Cross-Platform .NET Applications with C# for Windows, macOS, Linux, iOS, Android and Browser.&quot; data-og-host=&quot;www.avaloniaui.net&quot; data-og-source-url=&quot;https://avaloniaui.net/&quot; data-og-url=&quot;http://www.avaloniaui.net/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bADu46/hyVjcxfKNw/0S11aDB1Z2oNmoa1NTNjW0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/EEiEp/hyVja7gBvJ/bb0R3T9VGDIMytiFRvsrg0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/cDXcBo/hyVjlgDec4/7VqKs6D36AqGtbJe82uwHK/img.png?width=1358&amp;amp;height=764&amp;amp;face=0_0_1358_764&quot;&gt;&lt;a href=&quot;https://avaloniaui.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://avaloniaui.net/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bADu46/hyVjcxfKNw/0S11aDB1Z2oNmoa1NTNjW0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/EEiEp/hyVja7gBvJ/bb0R3T9VGDIMytiFRvsrg0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/cDXcBo/hyVjlgDec4/7VqKs6D36AqGtbJe82uwHK/img.png?width=1358&amp;amp;height=764&amp;amp;face=0_0_1358_764');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Avalonia UI - Cross-Platform UI Framework for .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Pixel-Perfect Cross-Platform .NET Applications with C# for Windows, macOS, Linux, iOS, Android and Browser.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.avaloniaui.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) .NET및 실행환경에 대한 정보 파악하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 설치된 .NET SDK및 runtime과 함께 운영체제에 대한 정보를 확인해 보려면 'dotnet --info'명령을 사용하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clOL2H/btsEORXMgPm/efE8L76BnYpck5wFvn2PB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clOL2H/btsEORXMgPm/efE8L76BnYpck5wFvn2PB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clOL2H/btsEORXMgPm/efE8L76BnYpck5wFvn2PB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclOL2H%2FbtsEORXMgPm%2FefE8L76BnYpck5wFvn2PB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;1426&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) dotnet CLI로 Project 관리하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET CLI는 아래명령을 통해 project를 관리하기 위해 현재 folder에 project에 대한 몇 가지 작업을 수행할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dotnet help : dotnet 명령에 대한 도움말을 표시합니다.&lt;/li&gt;
&lt;li&gt;dotnet new : 새로운 .NET project 또는 file을 생성합니다.&lt;/li&gt;
&lt;li&gt;dotnet tool : .NET 환경을 확장하기 위한 도구를 설치하거나 관리합니다.&lt;/li&gt;
&lt;li&gt;dotnet workload : .NET MAUI와 같은 선택적 workload를 관리합니다.&lt;/li&gt;
&lt;li&gt;donet restore : project에 대한 의존성을 download 합니다.&lt;/li&gt;
&lt;li&gt;dotnet build : .NET Project를 build(compile)합니다. .NET 8에서는 --tl라는 새로운 option을 추가했으며 이를 통해 build가 수행중인 작업에 대한 실시간 정보를 확인할 수 있습니다. 좀더 자세한 사항은 &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-build#options&quot;&gt;dotnet build command - .NET CLI | Microsoft Learn&lt;/a&gt; 이곳을 참고하시기 바랍니다.&lt;/li&gt;
&lt;li&gt;dotnet build-server : build에 의해 시작된 server와의 상호작용을 수행합니다.&lt;/li&gt;
&lt;li&gt;dotnet msbuild : MS Build Engine명령을 실행합니다.&lt;/li&gt;
&lt;li&gt;dotnet clean : build로 생성되는 임시 folder를 삭제합니다.&lt;/li&gt;
&lt;li&gt;dotnet test : project에 대한 단위 test를 build 하고 실행합니다.&lt;/li&gt;
&lt;li&gt;dotnet run : project를 build하고 실행합니다.&lt;/li&gt;
&lt;li&gt;dotnet pack : project에 대한 NuGet package를 생성합니다.&lt;/li&gt;
&lt;li&gt;dotnet publish : 의존성 또는 self-contained application과 함께 project를 build하고 게시합니다. .NET 7및 그 이전 version에서는 배포시 Debug의 설정이 기본이었으나 .NET 8부터는 Release설정이 기본으로 게시합니다.&lt;/li&gt;
&lt;li&gt;dotnet add : project에 package 혹은 class library에 대한 참조를 추가합니다.&lt;/li&gt;
&lt;li&gt;dotnet remove : project에 대한 package 혹은 class library 참조를 삭제합니다.&lt;/li&gt;
&lt;li&gt;dotnet list : project에 참조된 package 또는 class library를 listing 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Self-contained app 게시하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만든 cross-platform console app인 DotNetEverywhere를 게시하려면 terminal에서 DotNetEverywhere project가 있는(csproj가 있는) folder로 이동한 뒤 다음과 같은 명령을 내려 project를 build 하고 self-contained release version으로 Windows 10에서의 console application을 게시하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;publish&amp;nbsp;-c&amp;nbsp;Release&amp;nbsp;-r&amp;nbsp;win10-x64&amp;nbsp;--self-contained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 build engine은 필요한 모든 package를 복구하고 project source code를 assembly DLL로 compile 하여 publish folder를 만들어 저장하게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Determining&amp;nbsp;projects&amp;nbsp;to&amp;nbsp;restore... &lt;br /&gt;All projects are up-to-date for restore. &lt;br /&gt;DotNetEverywhere -&amp;gt; C:\Users\w10tsp\DotNetEverywhere\bin\Release\net7.0\win10-x64\DotNetEverywhere.dll &lt;br /&gt;DotNetEverywhere -&amp;gt; C:\Users\w10tsp\DotNetEverywhere\bin\Release\net7.0\win10-x64\publish\&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 아래 명령을 추가로 사용해 macOS와 다양한 Linux에서의 release version으로 게시하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet publish -c Release -r osx-x64 --self-contained&lt;br /&gt;dotnet publish -c Release -r osx.11.0-arm64 --self-contained&lt;br /&gt;dotnet publish -c Release -r linux-x64 --self-contained&lt;br /&gt;dotnet publish -c Release -r linux-arm64 --self-contained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;필요하다면 위와 같은 명령들은 PowerShell과 같은 script 언어를 사용해 자동화하고 cross-platform인 PowerSheel Core를 사용해 모든 OS상에서 script file을 실행할 수 있습니다. 이는 그저 위의 명령들을 저장한 .ps1 확장자 file을 만들고 file을 실행하기만 하면 됩니다. PowerShell에 관해서는 아래 link를 통해 더 자세한 정보를 확인하실 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/markjprice/cs11dotnet7/tree/main/docs/powershell&quot;&gt;cs11dotnet7/docs/powershell at main &amp;middot; markjprice/cs11dotnet7 &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687486368105&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - markjprice/cs11dotnet7: Repository for the Packt Publishing book titled &amp;quot;C# 11 and .NET 7 - Modern Cross-Platform Devel&quot; data-og-description=&quot;Repository for the Packt Publishing book titled &amp;quot;C# 11 and .NET 7 - Modern Cross-Platform Development Fundamentals&amp;quot; by Mark J. Price - GitHub - markjprice/cs11dotnet7: Repository for the ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/markjprice/cs11dotnet7/tree/main/docs/powershell&quot; data-og-url=&quot;https://github.com/markjprice/cs11dotnet7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lTc20/hyS5tHyhvD/fcGKJRbF2IssB30XMKEtl1/img.png?width=1200&amp;amp;height=600&amp;amp;face=937_99_1058_232&quot;&gt;&lt;a href=&quot;https://github.com/markjprice/cs11dotnet7/tree/main/docs/powershell&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/markjprice/cs11dotnet7/tree/main/docs/powershell&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lTc20/hyS5tHyhvD/fcGKJRbF2IssB30XMKEtl1/img.png?width=1200&amp;amp;height=600&amp;amp;face=937_99_1058_232');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - markjprice/cs11dotnet7: Repository for the Packt Publishing book titled &quot;C# 11 and .NET 7 - Modern Cross-Platform Devel&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Repository for the Packt Publishing book titled &quot;C# 11 and .NET 7 - Modern Cross-Platform Development Fundamentals&quot; by Mark J. Price - GitHub - markjprice/cs11dotnet7: Repository for the ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령을 실행한 뒤 Windows 탐색기를 열어 'DotNetEverywhere\bin\ Release\net7.0' folder로 이동해 보면 다섯 가지 OS에 대한 output folder가 생성되어 있음을 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'win10-x64' folder안에서 publish folder를 선택해 보면 Microsoft.CSharp.dll과 같은 필요한 모든 assembly가 저장되어 있음을 볼 수 있으며 이중 DotNetEverywhere.exe file을 선택하면 대략 file의 크기가 151 KB정도 됨을 보실 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1163&quot; data-origin-height=&quot;701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xdutt/btsEIRB6gSH/BI0EeEmdSZtDg2IXuB1xtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xdutt/btsEIRB6gSH/BI0EeEmdSZtDg2IXuB1xtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xdutt/btsEIRB6gSH/BI0EeEmdSZtDg2IXuB1xtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxdutt%2FbtsEIRB6gSH%2FBI0EeEmdSZtDg2IXuB1xtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1163&quot; height=&quot;701&quot; data-origin-width=&quot;1163&quot; data-origin-height=&quot;701&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 OS가 Windows라면 해당 file을 double click 하여 program을 실행하고 그 결과를 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;I&amp;nbsp;can&amp;nbsp;run&amp;nbsp;everywhere! &lt;br /&gt;OS&amp;nbsp;Version&amp;nbsp;is&amp;nbsp;Microsoft&amp;nbsp;Windows&amp;nbsp;NT&amp;nbsp;10.0.19045.0. &lt;br /&gt;I&amp;nbsp;am&amp;nbsp;Windows&amp;nbsp;10. &lt;br /&gt;Press&amp;nbsp;ENTER&amp;nbsp;to&amp;nbsp;stop&amp;nbsp;me.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 publish folder의 전체 크기는 대략 70MB 정도 됩니다. 각 OS별로 만들어진 publish folder를 필요한 OS에 복사하고 실행하면 아마도 정상적으로 실행됨을 알 수 있을 것입니다. 이는 console app이 .NET application에 대한 self-contained 배포판이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 console app을 사용했지만 ASP.NET Core website나 web service 또는 Windows Forms나 WPF app과 같은 것들도 쉽게 만들 수 있습니다. 물론 이때도 Linux나 macOS가 아닌 Windows computer를 위한 Windows desktop app을 배포할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 단일 file app 게시하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 file로서 게시하고자 한다면 게시할 때 flag를 지정할 수 있습니다. .NET 5에서 단일 file app은 Windows와 macOS에서 사실상 기술적으로 단일 file 배포가 불가능했기 때문에 주로 Linux에 집중되었습니다. 그러나 .NET 6 이후에부터는 Windows에서도 적절한 단일 file app을 만들 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 app을 실행하고자 하는 computer에 적절한 version의 .NET이 설치되어 있다면 app을 게시할 때 아래와 같이 확장 flag를 사용해 self-contained가 필요하지 않고 단일 file로서 배포(가능하다면)하고자 함을 알려줄 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;publish&amp;nbsp;-r&amp;nbsp;win10-x64&amp;nbsp;-c&amp;nbsp;Release&amp;nbsp;--no-self-contained&amp;nbsp;/p:PublishSingleFile=true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 2개의 file이 생성되는데 하나는 exe, 다른 하나는 pdb입니다. exe는 실행가능한 file을 의미하며 pdb는 program debug database로서 debugging에 필요한 정보를 담고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS상에서 게시된 application애 대한 exe는 존재하지 않으므로 osx-x64와 같은 명령을 사용해야 하며 생성된 file에는 확장자가 존재하지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 pdb file마저도 exe안으로 포함시키고자 한다면 assembly와 함께 배포되도록 해야 하며 그러기 위해서 csproj file에서 &amp;lt;PropertyGroup&amp;gt; 요소 안에 &amp;lt;DebugType&amp;gt; 요소를 추가하고 여기에 embedded를 아래와 같이 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687501875543&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;RuntimeIdentifiers&amp;gt;
	win10-x64;osx-x64;osx.11.0-arm64;linux-x64;linux-arm64
&amp;lt;/RuntimeIdentifiers&amp;gt;
&amp;lt;DebugType&amp;gt;embedded&amp;lt;/DebugType&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 대상 computer에 .NET이 설치되어 있지 않은 상태라면 Linux의 경우 단 2개의 file만을 필요로 하지만 Windows라면 coreclr.dll, clrjit.dll, clrcompression.dll 그리고 mscordaccore.dll과 같은 추가적인 file이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;terminal에서 아래 명령을 통해 Windows 10용 console app의 self-contained release version을 build 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;publish&amp;nbsp;-c&amp;nbsp;Release&amp;nbsp;-r&amp;nbsp;win10-x64&amp;nbsp;--self-contained&amp;nbsp;/p:PublishSingleFile=true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DotNetEverywhere\bin\Release\net7.0\win10-x64\publish foler에서 DotNetEverywhere.exe file을 선택합니다. 그러면 해당 file의 크기가 거의 65MB 정도로 커지게 되는 것을 알 수 있습니다. 또한 pdb file은 11KB 정도인데 이는 system에 따라 크기가 달라질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) App trimming을 사용한 app size 줄이기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET app을 self-contained app으로 배포하는 데 있어서 한 가지 문제점은 .NET library에서 너무 많은 용량을 차지한다는 것이며 특히 문제는 모든 .NET Library를 brwoser에서 내려받아야 하는 Blazor WebAssembly component가 문제가 될 수 있고 이러한 여러 가지 이유로 size를 줄여줄 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이러한 문제는 배포 시 사용하지 않는 assembly를 packaging에서 제외하도록 함으로써 해결할 수 있습니다. .NET Core 3.0에서 도입된 app trimming system은 code에서 필요한 assembly를 식별하고 필요하지 않은 것들을 제거할 수 있습니다. 이를 copyused trim mode라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 5에서 trimming은 개별 type을 제거하고 심지어 assembly안에서 사용되지 않는 method등의 member를 제거할 수도 있습니다. template에 의해 생성된 기본 console app의 경우 trimming된 System.Console.dll assembly는 대략 60KB에서 30KB로 줄어드는데 .NET 5에서 당시 이 기능은 실험적인 기능이라 기본적으로 사용되지 않도록 disable되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6에서 Microsoft는 자신들의 library에 주석을 추가하여 어떤 식으로 안전하게 trimming 할 수 있는지를 나타내도록 하였으며 이에 따라 type과 member의 trimming이 기본적으로 사용되도록 적용되었고 이를 link trim mode라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서 Microsoft는 link를 full로 copyused를 partial로 명칭을 변경하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 trimming이 사용되지 않는 assembly, type, member 등을 얼마나 잘 식별하느냐 하는 것입니다. 만약 code가 동적인 것, 예를 들어 reflection과 같은 것이라면&amp;nbsp; 정확하게 작동하지 않을 수 있고 이것 때문에 Microsoft는 수동적인 조정이 가능하도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Assembly 수준 trimming 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assembly 수준의 trimming을 사용하는 데는 2가지 방법이 있습니다. 첫 번째는 csproj(project file)에 아래와 같은 요소를 추가하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687574186787&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PublishTrimmed&amp;gt;true&amp;lt;/PublishTrimmed&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 게시할 때 아래와 같이 flag를 사용하는 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;publish&amp;nbsp;-c&amp;nbsp;Release&amp;nbsp;-r&amp;nbsp;win10-x64&amp;nbsp;--self-contained&amp;nbsp;-p:PublishTrimmed=True&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Type 및 member수준의 trimming 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에도 2가지 방법이 있는데 첫 번째는 위의 예와 마찬가지로 아래의 요소를 추가하는 것이며&lt;/p&gt;
&lt;pre id=&quot;code_1687574412999&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PublishTrimmed&amp;gt;true&amp;lt;/PublishTrimmed&amp;gt;
&amp;lt;TrimMode&amp;gt;Link&amp;lt;/TrimMode&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 방법으로 flag를 사용하는 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet publish ... -p:PublishTrimmed=True -p:TrimMode=Link&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6에서 link trim mode는 기본적으로 사용할 수 있으므로 assembly수준의 trimming을 의미하는 copyuse와 같은 다른 trim mode를 사용하고자 하는 경우일 때만 swich를 지정하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) build artifact가 생성되는 위치 조정하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 각 project는 자신만의 obj와 bin folder를 가지고 있어서 여기에 build 동안의 임시 file이 생성되며 특히 bin은 배포를 위한 file이 생성됩니다. .NET 8에서는 필요하다면 obj와 bin에 생성되는 file의 위치를 변경할 수도 있는 기능을 도입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project가 존재하는(*.csproj file이 존재하는) folder에서 아래와 같이 명령을 내려줍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;new&amp;nbsp;buildprops&amp;nbsp;--use-artifacts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 특별한 경우가 아닌한 다음과 같이&amp;nbsp; message가 표시되며&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5Yl8X/btsES25fd3W/sT5pJ7ClN3fQT75g1X9oEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5Yl8X/btsES25fd3W/sT5pJ7ClN3fQT75g1X9oEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5Yl8X/btsES25fd3W/sT5pJ7ClN3fQT75g1X9oEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5Yl8X%2FbtsES25fd3W%2FsT5pJ7ClN3fQT75g1X9oEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1636&quot; height=&quot;210&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 folder에는 'Directory.Build.props'이름의 file이 표시됩니다. file은 다음과 같은 makeup으로 저장되어 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707965559278&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project&amp;gt;
  &amp;lt;!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build --&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;

    &amp;lt;ArtifactsPath&amp;gt;$(MSBuildThisFileDirectory)artifacts&amp;lt;/ArtifactsPath&amp;gt;

  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 설정은 build process가 시작되면 그에 따른 folder가 artifacts 이름의 folder안에 생성됨을 의미합니다. 따라서 project를 build해 보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzddCD/btsEP77AJl1/paXyt2qlVGn4tqn5pwhUfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzddCD/btsEP77AJl1/paXyt2qlVGn4tqn5pwhUfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzddCD/btsEP77AJl1/paXyt2qlVGn4tqn5pwhUfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzddCD%2FbtsEP77AJl1%2FpaXyt2qlVGn4tqn5pwhUfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;634&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bin과 obj folder가 위와 같이 생성되어 있음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) Native ahead-of-time compilation&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT는 .NET이 설치되어 있지 않은 환경에서의 실행을 위해 Self-contained 형태로 assembly를 생성하고 그것을 native code로 compile하여 더 빠르게 실행하고 더 적은 memory를 사용할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, runtime에서 JIT(Just In Time) compiler를 사용하는 대신 배포시점에 IL code를 native code로 compile하는 것인데 위에서 설명한 이점을 얻을 수 있는 대신 Windows x64나 Linux Arm과 같이 특정한 runtime환경이 지정되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;native AOT는 게시시점에 발생하므로 현재 project가 AOT를 사용하도록 설정되어 있다고 하더라도 debugging의 목적으로 실행하는등의 동작시에는 여전히 native AOT가 아닌 runtime JIT compiler를 사용하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 native AOT와 호환되지 않는 일부 기능은 disable되거나 예외를 발생시킬 수 있으며 source 분석기가 활성화되어 잠재적인 code 비호환성에 대한 경고를 표시하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Native AOT 의 한계&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT는 다음과 같은 일부 한계가 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Assembly의 동적 loading이 없습니다.&lt;/li&gt;
&lt;li&gt;System.Reflection.Emit을 사용하는 것과 같은 runtime code 생성이 없습니다.&lt;/li&gt;
&lt;li&gt;Trimming이 필요하며 여기에는 자체적인 한계가 존재합니다.&lt;/li&gt;
&lt;li&gt;필요한 모든 library를 포함하는 Self-contained으로만 배포되므로 배포크기가 커질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 자체 assembly에서 위의 기능을 사용하지 않을 수 있지만 .NET 자체의 주요부분은 해당 기능을 사용합니다. 예를 들어 ASP.NET Core MVC(Controller를 사용하는 Web API service를 포함해)와&amp;nbsp; EF Core는 자신들의 기능을 구현하기 위해 runtime code 생성을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Team은 native AOT와 .NET과의 더 나은 호환성을 위해 노력중이지만 Minimal API를 사용하는 경우에만 .NET 8에서 ASP.NET Core에 대한 기본적인 지원을 포함하고 있으며 EF Core는 지원하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT 게시 process는 지원하지 않는 기능을 사용하는 경우 이를 알려주기 위한 code 분석기를 포함하고 있지만 아직까지 모든 package가 이러한 기능이 잘 작동하도록 anotate되어 있지는 않습니다. Type또는 member가 AOT를 지원하지 않음을 나타내는데 사용된 가장 일반적인 annotation은 [RequiresDynamicCode] attribute입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOT가 나타내는 위험성에 관한 자세한 사항은 아래 link를 참고하시가 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot;&gt;Introduction to AOT warnings - .NET | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1707983031201&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Introduction to AOT warnings - .NET&quot; data-og-description=&quot;Learn why warnings might be produced when publishing an application as Native AOT, how to address them, and how to make the application &amp;quot;AOT compatible.&amp;quot;&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/csUYKO/hyVm5wMfR8/QAKJgQZYjbMRjtu0kAbpv0/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/csUYKO/hyVm5wMfR8/QAKJgQZYjbMRjtu0kAbpv0/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Introduction to AOT warnings - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn why warnings might be produced when publishing an application as Native AOT, how to address them, and how to make the application &quot;AOT compatible.&quot;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Reflection 과 native AOT&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reflection는 runtime에서 type의 metadata를 검사하고 member의 동적호출과 code생성을 목적으로 자주사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT는 일부 reflection기능을 허용하긴 하지만 native AOT compilation process동안 수행되는 trimming은 type이&amp;nbsp; reflection을 통해서만 접근가능한 member를 가지고 있는지의 여부를 정적으로 판단할 수 없고 따라서 이들 member는 AOT에 의해 제거될 수 있고 때문에 runtime예외를 일으킬 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개발자는 자신의 type에 [DynamicallyAccessedMembers]으로 annotate함으로서 해당 member가 reflection을 통해서만 접근될 수 있고 때문에 trimming하지 않은 상태로 남아있어야 함을 표시할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Native AOT의 요구사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT는 운영체제에 따라 다음과 같은 추가적인 요구사항이 뒤따릅니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6279%;&quot;&gt;Windows&lt;/td&gt;
&lt;td style=&quot;width: 83.3721%;&quot;&gt;전체 기본 component를 가진 Visual Studio 2022 Desktop development with C++ workload 설치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6279%;&quot;&gt;Linux&lt;/td&gt;
&lt;td style=&quot;width: 83.3721%;&quot;&gt;compiler toolchain과 .NET runtime이 의존하는 library를 위해 developer package를 설치해야 합니다. 예를 들어 Ubuntu 18.04 이상에서 다음과 같은 명령을 내려줍니다.&lt;br /&gt;&lt;br /&gt;sudo apt-get install clang zlib1g-dev&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Cross-platform native AOT 배포는 지원하지 않습니다. 즉, 배포하고자 하는 특정 운영제제상에서 배포되어야 합니다. 때문에 Linux에서의 native AOT project 배포본은 Windows상에서 실행될 수 없습니다. 물론 그 반대도 마찬가지 입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Project에 native AOT 적용하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project에서 native AOT 배포를 적용하려면 project (csproj) file에 다음과 같이 &amp;lt;PublishAot&amp;gt;요소를 추가해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708045143269&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net8.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;PublishAot&amp;gt;true&amp;lt;/PublishAot&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Native AOT project build 하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio를 통해 native AOT 호환 project를 생성하려면 다음과 같이 'Enable native AOT publish'를 check합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBMVsk/btsEWx4Olb2/x2SkYhAfzJXkdVWfmb3YN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBMVsk/btsEWx4Olb2/x2SkYhAfzJXkdVWfmb3YN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBMVsk/btsEWx4Olb2/x2SkYhAfzJXkdVWfmb3YN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBMVsk%2FbtsEWx4Olb2%2Fx2SkYhAfzJXkdVWfmb3YN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1014&quot; height=&quot;675&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dotnet CLI를 통해서는 다음과 같이 --aot option을 통해 project를 생성할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet new console --aot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방법으로 생성된 project file에서는 PublishAot와 InvariantGlobalization요소가 추가됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708046478062&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PropertyGroup&amp;gt;
  &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
  &amp;lt;TargetFramework&amp;gt;net8.0&amp;lt;/TargetFramework&amp;gt;
  &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
  &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
  &amp;lt;PublishAot&amp;gt;true&amp;lt;/PublishAot&amp;gt;
  &amp;lt;InvariantGlobalization&amp;gt;true&amp;lt;/InvariantGlobalization&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET 8의 Console App project에서는 명시적으로 invariant globalization를 true로 설정하는 것은 새로운 기능으로 이는 app이 특정한 문화권에 구애받지 않고 전세계 어디서든 배포되고 동일한 동작을 수행하도록 합니다. 만약 이 요소의 설정을 false로 하거나 요소자체를 누락한다면 현재 computer의 문화권을 기본으로 설정하게 됩니다. 이에 관한 좀더 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md&quot;&gt;runtime/docs/design/features/globalization-invariant-mode.md at main &amp;middot; dotnet/runtime &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어 program.cs에서는 기존의 문을 모두 삭제하고 현재 문화권과 OS version을 표시하도록 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708047913049&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Globalization;

Console.WriteLine(&quot;현재 문화권 설정: {0}&quot;, CultureInfo.CurrentCulture. DisplayName);
Console.WriteLine(&quot;운영체제: {0}&quot;, Environment.OSVersion);

Console.Write(&quot;종료하려면 아무키나 누르십시오...&quot;);
Console.ReadKey(intercept: true);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project를 실행하고 결과를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjIXZO/btsEVeSsTAr/2c5ca77tuf5SUcPuxNb3ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjIXZO/btsEVeSsTAr/2c5ca77tuf5SUcPuxNb3ZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjIXZO/btsEVeSsTAr/2c5ca77tuf5SUcPuxNb3ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjIXZO%2FbtsEVeSsTAr%2F2c5ca77tuf5SUcPuxNb3ZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;628&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 native AOT는 배포시점에 적용되므로 build된 실행 file은 여전히 JIT compiler를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Native AOT project 배포하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발중에 정상적으로 작동하는 console app이라도 trimming하지 않고 JIT compile하게 되면 이를 native AOT를 사용해 배포하는 것은 실패할 수 있습니다. 일단 trimming이 이루어지고 JIT compile되어야 native AOT 배포를 사용할 수 있는 상태가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포동안에 project가 AOT에 관한 어떠한 경고도 표시하지 않는다면 해당 app은 AOT 배포후에 잘 작동하리라고 기대할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project를 배포하려면 다음과 같이 명령을 내려줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BMRno/btsEVMVCzu6/pKzxF5zVIwhtvkqUGkrNv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BMRno/btsEVMVCzu6/pKzxF5zVIwhtvkqUGkrNv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BMRno/btsEVMVCzu6/pKzxF5zVIwhtvkqUGkrNv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBMRno%2FbtsEVMVCzu6%2FpKzxF5zVIwhtvkqUGkrNv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;248&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포실행 후 'bin\Release\net8.0\win-x64\publish' folder를 보면 실행 file이 1.2M가 넘는것을 확인할 수 있습니다. 참고로 pdb file은 debuging에 사용되는 file입니다. 여기에 있는 해당 exe file을 실행하면 이전과 정확히 같은 동작을 수행한다는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 program.cs로 돌아와 이번에는 동적 code를 위한 namespaces를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708050026530&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection; // To AssemblyName.
using System.Reflection.Emit; // To AssemblyBuilder.

using System.Globalization;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 동적 assembly builder를 생성하도록 아래와 같이 code를 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1708050118384&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(&quot;assemblyName&quot;), AssemblyBuilderAccess.Run);

Console.WriteLine(&quot;현재 문화권 설정: {0}&quot;, CultureInfo.CurrentCulture. DisplayName);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 배포를 시도하면 아래와 같은 경고 message를 보게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Np4LT/btsETpUAHNn/LDLikB9AyL6KMmJZnhY6JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Np4LT/btsETpUAHNn/LDLikB9AyL6KMmJZnhY6JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Np4LT/btsETpUAHNn/LDLikB9AyL6KMmJZnhY6JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNp4LT%2FbtsETpUAHNn%2FLDLikB9AyL6KMmJZnhY6JK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;400&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략적인 내용은 .NET team 이 [RequiresDynamicCode]&amp;nbsp; attribute를 적용한 DefineDynamicAssembly method의 호출에 관한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;native AOT에 관한것은 아래 linki에서 좀더 자세하게 알아보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net7%2Cwindows&quot;&gt;Native AOT deployment overview - .NET | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708051126534&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Native AOT deployment overview - .NET&quot; data-og-description=&quot;Learn what Native AOT deployments are and why you should consider using it as part of the publishing your app with .NET 7 and later.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net7%2Cwindows&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kkKUx/hyVmTpHZ1B/SLwnhhg7rzjPihpHUdvbT0/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net7%2Cwindows&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net7%2Cwindows&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kkKUx/hyVmTpHZ1B/SLwnhhg7rzjPihpHUdvbT0/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Native AOT deployment overview - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn what Native AOT deployments are and why you should consider using it as part of the publishing your app with .NET 7 and later.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;4. .NET assembly decompile&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#을 학습하기 위한 가장 좋은 방법 중에 하나는 수준 높은 다른 누군가가 만들어 놓은 code를 보는 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;물론 학습목적이 아닌 자신의 library나 application에 사용할 목적으로 code를 복사하기 위한 decompile을 시도해 볼 수도 있을 것입니다. 어쨌건 decompile은 그들의 지적재산권을 침해하는 것으로 귀결될 수 있으므로 주의해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Visual Studio 2022의 ILSpy extension을 사용한 decompile&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET assembly의 decompile은 ILSpy와 같은 도구를 통해 가능합니다. Visual Studio에서 Extensions -&amp;gt; Manage Extensions menu를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 category에서 online을 선택하고 search에 'ILSpy'를 입력하고 ILSpy 2022 선택한 뒤 download button을 눌러줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8FTNU/btsEGSamrmc/oekaGzCkQGdP7lxWgSahI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8FTNU/btsEGSamrmc/oekaGzCkQGdP7lxWgSahI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8FTNU/btsEGSamrmc/oekaGzCkQGdP7lxWgSahI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8FTNU%2FbtsEGSamrmc%2FoekaGzCkQGdP7lxWgSahI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;941&quot; height=&quot;653&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Download가 완료되면 Visual Studio를 닫아 내려받은 ILSpy 2022의 설치를 완료합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GDZee/btsEIQiSWvI/20KkDFFSmudE3DaXW0qyE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GDZee/btsEIQiSWvI/20KkDFFSmudE3DaXW0qyE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GDZee/btsEIQiSWvI/20KkDFFSmudE3DaXW0qyE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGDZee%2FbtsEIQiSWvI%2F20KkDFFSmudE3DaXW0qyE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;332&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio를 재시작하고 csStudy07 project를 열어줍니다. 그리고 solution explorer에서 DotNetEverywhere project를 mouse오른쪽 click한뒤 'Open output in ILSpy'를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시가다리면 ILSpy가 열리게 되는데 decompile 할 언어설정이 C#으로 되어 있는지 확인합니다. 왼쪽 화면에는 'Assemblies navigation'화면이 보이는데 여기에서 DotNetEverywhere를 확장합니다. 이어서 '{ }'와 Program을 차례로 확장한 뒤 '&amp;lt;Main&amp;gt;$(string[]) : void'부분을 click 하여 compiler가 생성한 Program class와 보간문자열이 어떻게 작동하는지를 나타내는 &amp;lt;Main&amp;gt;$ method를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edtFWe/btsEFUmc724/8rGzVj5qV5NuBaj0YENfnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edtFWe/btsEFUmc724/8rGzVj5qV5NuBaj0YENfnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edtFWe/btsEFUmc724/8rGzVj5qV5NuBaj0YENfnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedtFWe%2FbtsEFUmc724%2F8rGzVj5qV5NuBaj0YENfnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;807&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 ILSpy에서 File -&amp;gt; Open menu를 선택한 뒤 'DotNetEverywhere/bin/Release/net7.0/linux-x64' foler에서 System.Linq.dll file을 열어봅니다. 그리고 Assemblies tree에서 'System.Linq ~'와 System.Linq namespace, Enumerable class를 차례로 확장한 뒤 'Count&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt;) : int'를 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WX38J/btsEF7r8MKJ/9PzbC9KTzX591osgy9nfi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WX38J/btsEF7r8MKJ/9PzbC9KTzX591osgy9nfi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WX38J/btsEF7r8MKJ/9PzbC9KTzX591osgy9nfi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWX38J%2FbtsEF7r8MKJ%2F9PzbC9KTzX591osgy9nfi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;807&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Count method를 보면 몇 가지 배워볼 만한 점을 발견할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;source 매개변수를 확인하고 null이라면 ArgumentNullException 예외를 발생시키도록 합니다.&lt;/li&gt;
&lt;li&gt;source는 좀 더 효휼적으로 읽어 들일 수 있는 자체의 Count속성으로 구현할 수 있는 Interface를 확인합니다.&lt;/li&gt;
&lt;li&gt;source의 모든 item을 열거하고 counter를 증가시키는 마지막 method는 구현 효율성이 가장 낮습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Count method의 source code를 확인하고&lt;/p&gt;
&lt;pre id=&quot;code_1687666908368&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static int Count&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source)
{
	if (source == null)
	{
		ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
	}
	if (source is ICollection&amp;lt;TSource&amp;gt; collection)
	{
		return collection.Count;
	}
	if (source is IIListProvider&amp;lt;TSource&amp;gt; iIListProvider)
	{
		return iIListProvider.GetCount(onlyIfCheap: false);
	}
	if (source is ICollection collection2)
	{
		return collection2.Count;
	}
	int num = 0;
	using IEnumerator&amp;lt;TSource&amp;gt; enumerator = source.GetEnumerator();
	while (enumerator.MoveNext())
	{
		num = checked(num + 1);
	}
	return num;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 Intermediate Language (IL)에서 생성한 것과 동일한 code를 비교해 보기 위해 toolbar에서 C#에 대한 dropdownlist를 ILSpy로 변경합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JHQNc/btsEHGtYPBO/H5DzQLkWqk4gKv6Z1ztWN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JHQNc/btsEHGtYPBO/H5DzQLkWqk4gKv6Z1ztWN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JHQNc/btsEHGtYPBO/H5DzQLkWqk4gKv6Z1ztWN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJHQNc%2FbtsEHGtYPBO%2FH5DzQLkWqk4gKv6Z1ztWN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;807&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C# compiler가 source code를 IL code로 변환시킨다는 것만 알고 있으면 될 뿐 사실 IL code가 딱히 유용하지는 않습니다. 위의 source code만 가지고도 Count method가 인수에 대해 어떻게 null을 확인하는지 등을 알 수 있으므로 훨씬 유용하게 활용될 수 있습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 link에서는 Visual Studio에서 ILSpy를 어떻게 사용할 수 있는지를 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/markjprice/cs11dotnet7/blob/main/docs/code-editors/vscode.md#decompiling-using-the-ilspy-extension-for-visual-studio-code&quot;&gt;cs11dotnet7/docs/code-editors/vscode.md at main &amp;middot; markjprice/cs11dotnet7 &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687667860974&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - markjprice/cs11dotnet7: Repository for the Packt Publishing book titled &amp;quot;C# 11 and .NET 7 - Modern Cross-Platform Devel&quot; data-og-description=&quot;Repository for the Packt Publishing book titled &amp;quot;C# 11 and .NET 7 - Modern Cross-Platform Development Fundamentals&amp;quot; by Mark J. Price - GitHub - markjprice/cs11dotnet7: Repository for the ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/markjprice/cs11dotnet7/blob/main/docs/code-editors/vscode.md#decompiling-using-the-ilspy-extension-for-visual-studio-code&quot; data-og-url=&quot;https://github.com/markjprice/cs11dotnet7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cVYXe0/hyS6CROL3i/mexhLzqpvAsIlS4JlZVWG0/img.png?width=1200&amp;amp;height=600&amp;amp;face=937_99_1058_232&quot;&gt;&lt;a href=&quot;https://github.com/markjprice/cs11dotnet7/blob/main/docs/code-editors/vscode.md#decompiling-using-the-ilspy-extension-for-visual-studio-code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/markjprice/cs11dotnet7/blob/main/docs/code-editors/vscode.md#decompiling-using-the-ilspy-extension-for-visual-studio-code&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cVYXe0/hyS6CROL3i/mexhLzqpvAsIlS4JlZVWG0/img.png?width=1200&amp;amp;height=600&amp;amp;face=937_99_1058_232');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - markjprice/cs11dotnet7: Repository for the Packt Publishing book titled &quot;C# 11 and .NET 7 - Modern Cross-Platform Devel&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Repository for the Packt Publishing book titled &quot;C# 11 and .NET 7 - Modern Cross-Platform Development Fundamentals&quot; by Mark J. Price - GitHub - markjprice/cs11dotnet7: Repository for the ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Visual Studio 2022를 통한 source link 보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Decompile대신 Visual Studio에는 source link를 사용해 본래 source code를 볼 수 있는 기능을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy07 solution에서 'SourceLinks'라는 Console app유형의 새로운 project를 생성하고 Program.cs에서 string변수를 선언하고 해당 변수가 가진 문자열의 수를 다른 변수에 할당하고 확인하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687674521169&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string name = &quot;cliel&quot;;
int length = name.Count();

Console.WriteLine($&quot;{name} has {length} characters.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Count method에서 mouse오른쪽 button을 click 하여 'Go To Implementation'을 선택합니다. 그러면 Count.cs이름의 source code file이 표시될 텐데 여기서 우리는 count와 관련된 다섯 개의 method가 Enumerable이라는 partial class에 구현되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3842&quot; data-origin-height=&quot;2169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kY51T/btsEFQRHcY2/mwQZ97kst8RZmd7GrCK0JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kY51T/btsEFQRHcY2/mwQZ97kst8RZmd7GrCK0JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kY51T/btsEFQRHcY2/mwQZ97kst8RZmd7GrCK0JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkY51T%2FbtsEFQRHcY2%2FmwQZ97kst8RZmd7GrCK0JK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3842&quot; height=&quot;2169&quot; data-origin-width=&quot;3842&quot; data-origin-height=&quot;2169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 source links를 통해서도 위 예제와 같이 관리의 용이성을 위해 어떻게 partial class를 통해서 class를 나누는지와 같은 상황별 예시를 확인할 수 있기 때문에 decompile 하는 것만큼 많은 것을 배울 수 있습니다. ILSpy를 사용하는 경우에도 물론 Enumerable class에 대한 수천 개의 method를 모두 볼 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 link를 통해서는 source links에 관한 더 많은 것과 얼마나 많은 NuGet package가 이를 지원할 수 있는지를 확인해 볼 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot;&gt;Source Link and .NET libraries | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687675249303&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Source Link and .NET libraries&quot; data-og-description=&quot;Best practice recommendations for using Source Link to improve debugging for .NET libraries.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bXAycN/hyS6yWgTXl/IJKLUTfup5w5qxcFFc3LnK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300,https://scrap.kakaocdn.net/dn/iUDN3/hyS6xiKg43/kRMPy1ohb3kr0B7xabcRCK/img.png?width=928&amp;amp;height=813&amp;amp;face=0_0_928_813&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bXAycN/hyS6yWgTXl/IJKLUTfup5w5qxcFFc3LnK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300,https://scrap.kakaocdn.net/dn/iUDN3/hyS6xiKg43/kRMPy1ohb3kr0B7xabcRCK/img.png?width=928&amp;amp;height=813&amp;amp;face=0_0_928_813');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Source Link and .NET libraries&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Best practice recommendations for using Source Link to improve debugging for .NET libraries.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;만약 source link가 작동하지 않으면 아래 설정에서 source link의 설정여부를 확인해 보시기 바랍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nAzcP/btsEXtIbW9D/Cp8djO77kjQ8tp10c9x6cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nAzcP/btsEXtIbW9D/Cp8djO77kjQ8tp10c9x6cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nAzcP/btsEXtIbW9D/Cp8djO77kjQ8tp10c9x6cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnAzcP%2FbtsEXtIbW9D%2FCp8djO77kjQ8tp10c9x6cK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;434&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 기술적으로 decompile을 막을 수 있는 방법은 없습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가는 compile 된 code에서 decompile을 막을 수는 없을까 하는 의문을 제기할 수도 있을 것입니다. 이 질문에 정확하게 답하자면 '그렇게 할 수 없다.'라고 할 수 있습니다. 대신 Dotfuscator와 같은 도구를 사용해 code를 읽기 어렵게 만들 수는 있지만 decompile자체는 막을 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 compile 된 application은 동작의 대상이 되는 platform, OS 그리고 hardware에 대한 명령을 포함하고 있습니다. 이들 명령은 기능적으로 본래 source code와 일치하지만 사람에게만 읽기 어렵게 만들수 있을 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source code를 보호하고자 한다면 여기에 debugger을 연결하고 단계적으로 code를 통과하는 것도 막을 수 있습니다. compile된 application이 pdb file을 가지고 있다면 debugger를 연결하고 구문을 한 줄씩 실행시킬 수도 있습니다. pdb file이 없어도 여전히 debugger를 연결하고 code가 어떻게 작동하는지를 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 decompile에 관한 이러한 내용은 C#, Visual Basic, F#과 같은 .NET언어 뿐만 아니라 C, C++, Delphi 및 assembly 언어(debugging에 연결하거나 disassembled 또는 decompile 될 수 있는)에도 해당하는 사실입니다. 아래 표는 해당 목적을 위해 사용하는 일부 도구를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 54px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.2403%; height: 20px;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%; height: 20px;&quot;&gt;도구&lt;/td&gt;
&lt;td style=&quot;width: 71.124%; height: 20px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.2403%; height: 17px;&quot;&gt;Debugger&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%; height: 17px;&quot;&gt;SoftICE&lt;/td&gt;
&lt;td style=&quot;width: 71.124%; height: 17px;&quot;&gt;운영체제(일반적으로 VM) 아래에서 실행됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.2403%; height: 17px;&quot;&gt;Debugger&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%; height: 17px;&quot;&gt;WinDbg&lt;/td&gt;
&lt;td style=&quot;width: 71.124%; height: 17px;&quot;&gt;다른 debugger보다 Windows data 구조에 관한 더 많은 것을 이해하는데 유용한 도구입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;Disassembler&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%;&quot;&gt;IDA Pro&lt;/td&gt;
&lt;td style=&quot;width: 71.124%;&quot;&gt;malware 분석등에 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;Decompiler&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%;&quot;&gt;HexRays&lt;/td&gt;
&lt;td style=&quot;width: 71.124%;&quot;&gt;IDA Pro의 plugin이며 C app을 decompile합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;Decompiler&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%;&quot;&gt;DeDe&lt;/td&gt;
&lt;td style=&quot;width: 71.124%;&quot;&gt;Delphi app을 decompile합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;Decompiler&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%;&quot;&gt;dotPeek&lt;/td&gt;
&lt;td style=&quot;width: 71.124%;&quot;&gt;JetBrains사에서 만든 것으로 .NET decompiler입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 누군가의 software를 Debugging, disassembling, decompiling 하는 행위는 license동의에 반하며 불법일 수 있습니다. 기술적인 방법으로 지적재산권을 보호하는 대신 법이 유일단 수단이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. .NET Framework 에서 .NET으로의 이식&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 .NET Framework 개발자라면 아마도 기존의 application을 .NET으로 전환하고자 할 수 있을 것입니다. 하지만 정말 .NET으로의 전환이 가장 좋은 방법인지는 심도 있게 고려해봐야 합니다. 단지 .NET으로의 이식만이 최상의 방법이 되지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 .NET Framework 4.8에서 동작하는 사용자가 그리 많지 않은 website project가 있습니다. 그러면 해당 website는 최소한의 hardware상에서 방문자 traffic은 충분히 제어되고 작 잘 동할 수 있을 텐데 이것을 .NET platform으로 전환하는데 잠재적으로 수개월을 소요하는 것은 시간낭비가 될 수 있습니다. 하지만 website가 현재 다수의 고비용 Windows Server가 필요하다면 더 비용이 적은 Linux server로 migration 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 전환 가능한 것&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 Windows, macOS, Linux에 대한 아래 type의 application을 지원하고 있으며 때문에 전환이 가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Razor Pages와 MVC를 포함한 ASP.NET Core web site&lt;/li&gt;
&lt;li&gt;Web API, Minimal API, OData를 포함하는 ASP.NET Core web service(REST/HTTP)&lt;/li&gt;
&lt;li&gt;gRPC, GraphQL, SignalR을 포함하는 ASP.NET Core-hoste&lt;/li&gt;
&lt;li&gt;명령행 interface인 Console App포함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 Windows에 대한 아래 Type의 application을 지원하고 있으며 운영에 적합이 가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows Forms applications&lt;/li&gt;
&lt;li&gt;Windows Presentation Foundation (WPF) applications&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 cross-platform desktop과 mobile device에 대한 아래 type의 application을 지원하고 있으며 전환이 가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mobile iOS와 Android를 위한 Xamarin app&lt;/li&gt;
&lt;li&gt;Desktop Windows와 macOS 또는 mobile iOS와 Android를 위한 .NET MAUI&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 아래 legacy Microsoft project에 대한 지원을 하지 않으며 전환이 불가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ASP.NET Web Forms website. (ASP.NET Core Razor Page 또는 Blazor로의 재구현이 필요합니다.)&lt;/li&gt;
&lt;li&gt;Windows Communication Foundation (WCF ) service (단, 요구사항에 따라 사용가능한 open-source project인 CoreWCF가 존재합니다.). (ASP.NET Core gRPC service로의 재구현이 필요합니다.)&lt;/li&gt;
&lt;li&gt;Silverligth application. (Blazor 또는 .NET MAUI로의 재구현이 필요합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Silverlight와 ASP.NET Web Forms application은 .NET으로의 전환이 불가능하며 기존의 Windows Forms와 WPF application을 WIndows전용의 .NET으로 전환하면 이에 따른 새로운 API와 고성능에 대한 이점을 가져갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Legacy ASP.NET MVC web application과 ASP.NET Web API web service역시 .NET으로의 전환이 가능하며 Windows, Linux 또는 macOS에서 host가 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 전환해야 하는 것&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전환이 가능하다면 그렇게 하는 것이 맞을까? 그렇게 한다면 어떤 이익을 얻을 수 있을까? 전환함으로써 얻을 수 있는 장점으로는 대략 다음과 같이 정리할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Website와 webservice에 대한 Linux, Docker 또는 Kubernetes로의 배포 : 해당 OS들은 website와 web service platform으로서 매우 가볍고 특히 Windows Server와 비교해서는 훨씬 비용 효과적이라고 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;IIS와 System.Web.dll에 대한 의존성 제거 : Windows Server로의 배포를 계속한다고 하더라도 ASP.NET Core는 Kestrel web server(또는 다른 것도 마찬가지)에서 가벼우면서도 고성능의 hosting이 가능합니다.&lt;/li&gt;
&lt;li&gt;명령줄 도구 : 개발자와 관리자가 자신들의 작업을 자동화하기 위해 사용하는 도구는 종종 console application으로 build 되는데 cross-platform에서 동작가능한 단일 도구는 그 자체로 매우 유용하게 사용될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) .NET Framework와 .NET의 차이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들 사이에는 대략 3가지 핵심적인 차이가 존재합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;.NET&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;.NET Framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;NuGet package로 배포되어 있으며 각 application은 필요한 .NET version의 자체 app local 복사를 통해 배포될 수 있습니다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System전반에 배포되어 있으며 assembly들은 공유됩니다.(GAC-Global Assembly Cache)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Component 계층으로 작게 나누어져 있으므로 최소한의 배포가 가능합니다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;단일적이며 획일적인 배포만이 가능합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;ASP.NET Web Forms와 같은 고전 기술과 AppDomains, .NET Remoting, binary serialization과 같은 비 cross-platform 기능들이 제거되었습니다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;.NET에서의 ASP.NET Core MVC와 같은 비슷한 기능뿐만 아니라 ASP.NET Web Forms와 같은 고전적인 기술까지도 남아 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) .NET Portability Analyzer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 기존의 application에 대한 이식성의 보고서를 생성하는 유용한 도구를 지원하고 있습니다. 아래 link에서 해당 도구의 시연장면을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot;&gt;A Brief Look at the .NET Portability Analyzer | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687770527535&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;A Brief Look at the .NET Portability Analyzer&quot; data-og-description=&quot;.NET is going through some monumental changes! It is often difficult to even begin to think about how to port our particular code to the various flavors of .NET. In this video Taylor Southwick and Connie Yau spend some time discussing a great tool that wil&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Z05DC/hyS6yC2ys6/GcVxoFpWnHbmElbGKt4kVk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Z05DC/hyS6yC2ys6/GcVxoFpWnHbmElbGKt4kVk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;A Brief Look at the .NET Portability Analyzer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET is going through some monumental changes! It is often difficult to even begin to think about how to port our particular code to the various flavors of .NET. In this video Taylor Southwick and Connie Yau spend some time discussing a great tool that wil&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) .NET Upgrade Assistant&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Legacy project를 .NET으로 upgrade 하기 위한 가장 최신의 도구로는 .NET Upgrade Assistant가 있습니다. 현재 다음과 같은 .NET Framework project type을 지원하고 있으며 향후에는 더 추가될 것으로 예상됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ASP.NET MVC&lt;/li&gt;
&lt;li&gt;Windows Forms&lt;/li&gt;
&lt;li&gt;WPF&lt;/li&gt;
&lt;li&gt;Console Application&lt;/li&gt;
&lt;li&gt;Class Library&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 dotnet 도구를 통해 .NET Upgrade Assistant를 설치하려면 아래의 명령을 내려줍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool install -g upgrade-assistant&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 link를 통해 .NET Upgrade Assistant에 대한 더 자세한 내용과 사용법을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot;&gt;Overview of the .NET Upgrade Assistant - .NET Core | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687771077397&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Overview of the .NET Upgrade Assistant - .NET Core&quot; data-og-description=&quot;Introducing the .NET Upgrade Assistant tool that helps upgrade .NET, .NET Core, or .NET Framework apps to the latest version of .NET.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dY4CmY/hyS8mucF5u/mZmveqrK566AAsVk4d1ltk/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456,https://scrap.kakaocdn.net/dn/sDIIl/hyS6AOoaQT/x2VDL28UPSKR6kuCTUWrlk/img.png?width=925&amp;amp;height=640&amp;amp;face=0_0_925_640,https://scrap.kakaocdn.net/dn/dEIL62/hyS6wFbuDd/dWVGqzblHyGXd0WU1XpYc1/img.png?width=995&amp;amp;height=442&amp;amp;face=0_0_995_442&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dY4CmY/hyS8mucF5u/mZmveqrK566AAsVk4d1ltk/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456,https://scrap.kakaocdn.net/dn/sDIIl/hyS6AOoaQT/x2VDL28UPSKR6kuCTUWrlk/img.png?width=925&amp;amp;height=640&amp;amp;face=0_0_925_640,https://scrap.kakaocdn.net/dn/dEIL62/hyS6wFbuDd/dWVGqzblHyGXd0WU1XpYc1/img.png?width=995&amp;amp;height=442&amp;amp;face=0_0_995_442');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Overview of the .NET Upgrade Assistant - .NET Core&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Introducing the .NET Upgrade Assistant tool that helps upgrade .NET, .NET Core, or .NET Framework apps to the latest version of .NET.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 비 .NET Standard library 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NuGet package는 .NET Standard 또는 .NET 7과 같은 version을 통해 compile 되지 않은 것이라 하더라도 대부분 .NET에서 사용될 수 있습니다. 만약 공식적으로 .NET Standard를 지원하지 않는 package라고 nuget.org web page에 표시된 package가 있어도 단정적으로 사용 못할 것이라 포기할 필요는 없습니다. 일단 시도해 보면서 되는지 안되는지의 여부를 판단해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같이 문서화된 것으로, Dialect Software LLC에서 제작된 행렬처리를 위한 사용자 지정 collection package는 .NET Core나 .NET 7이 존재하기 훨씬 이전인 2013년도가 마지막 update로 확인됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.nuget.org/packages/DialectSoftware.Collections.Matrix/&quot;&gt;NuGet Gallery | DialectSoftware.Collections.Matrix 1.0.0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687833904902&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;nugetgallery:package&quot; data-og-title=&quot;DialectSoftware.Collections.Matrix 1.0.0&quot; data-og-description=&quot;Provides matrix data structure suitable for storing elements in an n-dimensional cartesian coordinate system of positive integers. example: Axis x = new Axis(&amp;quot;x&amp;quot;, 0, 100, 1); Axis y = new Axis(&amp;quot;y&amp;quot;, 0, 10, 1); Matrix&amp;lt;long&amp;gt; matrix = new Matrix&amp;lt;long&amp;gt;(new []{x&quot; data-og-host=&quot;www.nuget.org&quot; data-og-source-url=&quot;https://www.nuget.org/packages/DialectSoftware.Collections.Matrix/&quot; data-og-url=&quot;https://nuget.org/packages/DialectSoftware.Collections.Matrix/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.nuget.org/packages/DialectSoftware.Collections.Matrix/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.nuget.org/packages/DialectSoftware.Collections.Matrix/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;DialectSoftware.Collections.Matrix 1.0.0&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Provides matrix data structure suitable for storing elements in an n-dimensional cartesian coordinate system of positive integers. example: Axis x = new Axis(&quot;x&quot;, 0, 100, 1); Axis y = new Axis(&quot;y&quot;, 0, 10, 1); Matrix&amp;lt;long&amp;gt; matrix = new Matrix&amp;lt;long&amp;gt;(new []{x&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.nuget.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 해당 package는 .NET Framework로 build 된 것이며 이와 같은 package가 .NET Standard에서만 가능한 API만 사용하는 경우 .NET project에서도 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AssembliesAndNamespaces project에서 아래와 같이 Dialect Software의 package를 참조 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687835234407&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include=&quot;DialectSoftware.Collections.Matrix&quot; Version=&quot;1.0.0&quot; /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 DialectSoftware.Collections과 DialectSoftware.Collections.Generics를 import 하고 아래와 같이 Axis와 Matrix&amp;lt;T&amp;gt;의 instance를 생성하고 값을 채워 넣은 다음 해당 값을 표시하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687835395061&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Axis x = new Axis(&quot;x&quot;, 0, 100, 1);
Axis y = new Axis(&quot;y&quot;, 0, 10, 1);

Matrix&amp;lt;long&amp;gt; matrix = new Matrix&amp;lt;long&amp;gt;(new[] { x, y });

int i = 0;
for (; i &amp;lt; matrix.Axes[0].Points.Length; i++)
{
    matrix.Axes[0].Points[i].Label = &quot;x&quot; + i.ToString();
}

i = 0;
for (; i &amp;lt; matrix.Axes[1].Points.Length; i++)
{
    matrix.Axes[1].Points[i].Label = &quot;y&quot; + i.ToString();
}

foreach (long[] c in matrix)
{
    matrix[c] = c[0] + c[1];
}

foreach (long[] c in matrix)
{
    Console.WriteLine(&quot;{0},{1} ({2},{3}) = {4}&quot;, matrix.Axes[0].Points[c[0]].Label, matrix.Axes[1].Points[c[1]].Label, c[0], c[1], matrix[c]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oUPkw/btsEFjT6cg5/D6T8KsEst2hdIZ7Kx5QQe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oUPkw/btsEFjT6cg5/D6T8KsEst2hdIZ7Kx5QQe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oUPkw/btsEFjT6cg5/D6T8KsEst2hdIZ7Kx5QQe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoUPkw%2FbtsEFjT6cg5%2FD6T8KsEst2hdIZ7Kx5QQe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. Preview 기능 사용하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 주기적으로 runtime, 언어 compiler, API library등 .NET의 많은 부분에 전역적으로 영향을 미치는 새로운 기능을 제공합니다. 그런데 실질적으로 Microsoft가 기능에 필요한 대부분의 작업을 완료했다고 하더라도 .NET release에 대한 주기 때까지는 배포가 이루어지지 않을 것입니다. 문제는 이렇게 되면 실전에서 적절한 test를 하기에는 너무 늦다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 .NET 6부터 Microsoft는 GA(general availability) release에서는 preview 기능을 포함하게 되었습니다. 개발자는 이들 미리 보기 기능을 선택하고 Microsoft에게 feedback을 제공할 수 있습니다. 최신 GA release는 모두가 사용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Preview 기능에 관한 내용임에 주의해야 합니다. .NET 혹은 Visual Studio에 대한 preview version과는 다릅니다. Microsoft는 Visual Studio와 .NET에 대한 preview version을 개발자들로부터 feedback을 얻고자 개발 중에 release 하며 그런 뒤 최종 GA를 release 합니다. 이때 GA가 모두가 사용가능한 기능이 되는 것입니다. GA이전에는 새로운 기능을 경험해 볼 수 있는 방법은 preview version을 설치하는 것입니다. Preview 기능은 GA release와 함께 설치되며 선택적으로 사용가능해야 하므로 다르다고 할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Microsoft는 .NET SDK 6.0.200를 2022년 02월에 release 했는데 이것은 C# 11 compiler를 preview 기능으로서 포함시켰습니다. 이는 .NET 6 개발자가 선택적으로 언어의 version을 preview로 설정할 수 있었음을 의미하며 이것으로 raw string literal과 required keyword 같은 C# 11 기능을 사용해 볼 수 있었습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Preivew 기능은 production code에서는 지원하지 않으며 최종 release전에 대폭적인 변화가 있을 가능성이 있습니다. 따라서 오로지 test목적으로만 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Preview 기능이 필요한 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[RequiresPreviewFeatures] attribute는 사용하는 assembly, type 또는 member를 나타내는 데 사용되며 Code analyzer는 해당 assembly를 scan 하고 필요하다면 경고를 생성하게 됩니다. 만약 code에서 어떠한 preview 기능도 사용하지 않는다면 어떠한 경고 message도 보지 않게 될 테지만 그렇지 않다면 preview 기능을 사용한다는 경고를 해당 code를 사용하는 이에게 표시하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Preview 기능 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csproj(project file)에서 아래와 같이 preview 기능과 preview 언어 기능을 사용하기 위한 요소를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687851759267&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&amp;lt;EnablePreviewFeatures&amp;gt;true&amp;lt;/EnablePreviewFeatures&amp;gt;
&amp;lt;LangVersion&amp;gt;preview&amp;lt;/LangVersion&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>.NET</category>
      <category>AOT</category>
      <category>c#</category>
      <category>Library</category>
      <category>NuGet</category>
      <category>package</category>
      <category>Preview</category>
      <category>publish</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/597</guid>
      <comments>https://cliel.tistory.com/entry/XFile#entry597comment</comments>
      <pubDate>Tue, 20 Feb 2024 09:11:50 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 6. Interface와 Class상속</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다.&amp;nbsp; 또한 generic을 사용하여 어떻게 code를 안전하게 만들고 성능을 높일 수 있는지, delegate와 event를 통해 type 간 message를 어떻게 교환할 수 있는지를 알아보고 참조와 값 type에 대한 차이점도 확인해 볼 것입니다. 공통기능에 대한 interface를 구현하고 기능을 재사용하기 위해 기반 class로부터 상속받는 파생 class를 만들 것이며 상속된 type member를 재정의하고 다형성(polymorphism)도 사용해 볼 것입니다. 또한 확장 method의 생성과 계층적으로 상속된 class간 변환에 대한 것들, 그리고&amp;nbsp; static code analyzer를 통해 어떻게 code를 개선할 수 있을지도 함께 살펴볼 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. Class library와 console application의 설정&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 예제를 위해서는 2개의 project를 정의하는 것으로 시작해야 합니다. csStudy06 이라는 solution을 만들고 여기에 MyLibrary이름의 Class Library유형의 project와 MyApp이름의 Console App project를 추가합니다. 그리고 MyLibrary file에서 Person.cs file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685665908653&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace MyLibrary
{
	public class Person
	{
		// 속성
		public string? Name { get; set; }
		public DateTime DateOfBirth { get; set; }
        
		// Method
		public void WriteToConsole()
		{
			Console.WriteLine($&quot;{Name} was born on a {DateOfBirth:dddd}.&quot;);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyApp project에서 MyLibrary project를 참조 추가하고 Program.cs에서 기존의 구문을 모두 삭제한 뒤 Person에 대한 instance개체를 생성하고 해당 개체의 정보를 console에 출력하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685666159731&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using MyLibrary;

Person kdh = new()
{
	Name = &quot;김동희&quot;,
	DateOfBirth = new(year: 1980, month: 12, day: 01)
};

kdh.WriteToConsole();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cf4cOX/btsD4TaZAAh/2mAK2FBNLkkz30zAuLg1Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cf4cOX/btsD4TaZAAh/2mAK2FBNLkkz30zAuLg1Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cf4cOX/btsD4TaZAAh/2mAK2FBNLkkz30zAuLg1Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf4cOX%2FbtsD4TaZAAh%2F2mAK2FBNLkkz30zAuLg1Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;71&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 정적 method와 연산자 다중정의(overloading)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연산자의 동작은 일반적으로 사용되는 method가 아닌 동일한 type에 대한 2개의 instance에 적용되는 특별한 method에 대한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 2개의 Person에 대한 intance에서 +나 &amp;times; 와 같은 이항연산자를 적용해 둘의 Person instance를 결합하여 '결혼식'을 표현거나 '출산'과 같이 곱하여 특별한 결과를 가져오기 위한 뭔가를 수행할 수 있을 것입니다. 이러한 동작은 대부분&amp;nbsp; 동작에 대한 의미가 가장 높은것에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;많은 경우에 비슷한 동작을 수행하는 정적 method와 instance method 둘다 구현하기도 합니다. 예를 들어 string type은 비교를 위한 Compare 정적 method와 CompareTo라는 instance method를 가지고 있습니다. 이는 type에서 더 높은 유연성을 제공함으로서 개발자가 type의 특정 기능을 사용하기에 적합한 방식을 선택할 수 있게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Method를 통한 기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 아래 2개의 field를 추가하여 Person이 결혼한 상태인지 누구랑했는지를 나타낼 수 있도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706595394491&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public List&amp;lt;Person&amp;gt; Spouses = new();
public List&amp;lt;Person&amp;gt; Children = new();
public bool Married =&amp;gt; Spouses.Count &amp;gt; 0;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 아래와 같은 method를 추가하는데 하나는 instance, 다른 하나는 정적 method이며 Person끼리 혼인이 가능하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706595634563&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void Marry(Person p1, Person p2)
{
  ArgumentNullException.ThrowIfNull(p1);
  ArgumentNullException.ThrowIfNull(p2);
  
  if (p1.Spouses.Contains(p2) || p2.Spouses.Contains(p1))
  {
    throw new ArgumentException(string.Format(&quot;{0}은(는) 이미 {1}와 혼인함&quot;, arg0: p1.Name, arg1: p2.Name));
  }
  
  p1.Spouses.Add(p2);
  p2.Spouses.Add(p1);
}

public void Marry(Person partner)
{
  Marry(this, partner);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 method에서는 p1과 p2이름의 parameter를 통해 Person 개체를 전달받고 있으며 이때 guard절을 통해 null여부를 확인하고 있습니다. 이 후 p1혹은 p2가 이미 혼인한 상태라면 예외를 발생시키고 그렇지 않으면 각각의 배우자로 서로를 저장하도록합니다. 물론 하나의 Person이 여러번의 혼인을 가능하게끔 할 수 있으며 그에 따라 예외가 발생하지 않도록 하는 것도 가능할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance method에서는 정적 method를 호출하는 것으로 되어 있으며 현재 Person(this)과 혼인 상대에 해당하는 Person을 매개변수로 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음의 별도의 instance method도 추가하여 Person이 혼인한 상태면 그에 따른 배우자와 자식을 표시할 수 있도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706597357915&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void OutputSpouses()
{
  if (Married)
  {
    Console.WriteLine($&quot;{Name}은(는) {Spouses.Count}명과 혼인함:&quot;);

    foreach (Person spouse in Spouses)
    {
      Console.WriteLine($&quot; {spouse.Name}&quot;);
    }
  }
  else
  {
    Console.WriteLine($&quot;{Name}은(는) 아직 싱글&quot;);
  }
}

public void WriteChildrenToConsole()
{
  Console.WriteLine($&quot;{Name}은(는) {Children.Count}명의 아이가 있음&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 아이를 갖게되는 경우를 위해 다음과 같은 instance method와 정적 method도 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706597861139&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Person Procreate(Person p1, Person p2)
{
  ArgumentNullException.ThrowIfNull(p1);
  ArgumentNullException.ThrowIfNull(p2);
  
  if (!p1.Spouses.Contains(p2) &amp;amp;&amp;amp; !p2.Spouses.Contains(p1))
  {
    throw new ArgumentException(string.Format(&quot;{0}은(는) 아직 {1}와 혼인상태가 아닙니다.&quot;, arg0: p1.Name, arg1: p2.Name));
  }
  
  Person baby = new()
  {
    Name = $&quot;{p1.Name}와 {p2.Name}의 Baby&quot;,
    DateOfBirth = DateTime.Now
  };
  
  p1.Children.Add(baby);
  p2.Children.Add(baby);
  
  return baby;
}

public Person ProcreateWith(Person partner)
{
    return Procreate(this, partner);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Procreate 정적 method에서 생성할 Person 개체는 p1과 p2 매개변수로 전달되며 여기서 baby라는 새로운 Person개체는 p1과 p2의 이름으로 생성됩니다. 이는 이후에 반환된 baby의 Name속성을 통해 변경할 수 있는데 물론 세번째 매개변수를 추가하여 생성할 baby의 이름을 지정해주는 것도 가능하지만 후에 이진 연산자를 정의할 것이므로 세번째 매개변수를 가질 수 없으며 따라서 baby 참조만을 반환하고 호출 code에서 이름을 바꾸도록 할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;baby개체는 부모에 해당하는 Person의 Children collection에 추가되고 반환될 것입니다. 이때 Class는 참조 type이므로 baby개체의 복사본이 아닌 memory에 저장된 baby개체의 참조가 저장됩니다. 참조 type과 값 type에 대한 자세한 사항은 추후에 살펴볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ProcreateWith이름의 instance method에서 Person개체는 this와 함께 partner이름의 매개변수로 정적 method인 Procreate 로 전달되어 해당 method구현을 재사용하게 됩니다. 여기서 this keyword는 현재 class의 intance 참조를 의미하며 관련된 정적 method와 instance method는 서로 다른 이름을 사용하는 것이 관례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 세명의 Person개체를 생성하고 결혼식을 올린다음 서로에 대한 Children을 생성합니다. 이때 예제에서 문자열을 처리하는 것중에 backslash (\) 접미사가 사용된것에 주의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706601689221&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person hong = new() { Name = &quot;홍길동&quot; };
Person kim = new() { Name = &quot;김길순&quot; };
Person lee = new() { Name = &quot;이길영&quot; };

hong.Marry(kim);

Person.Marry(hong, lee);
hong.OutputSpouses();
kim.OutputSpouses();
lee.OutputSpouses();

Person baby1 = hong.ProcreateWith(kim);
baby1.Name = &quot;Hong-1baby&quot;;
Console.WriteLine($&quot;{baby1.Name}아기가 {baby1.DateOfBirth}에 태어남&quot;);

Person baby2 = Person.Procreate(lee, hong);
baby2.Name = &quot;Hong-2baby&quot;;
kim.WriteChildrenToConsole();
lee.WriteChildrenToConsole();
hong.WriteChildrenToConsole();

for (int i = 0; i &amp;lt; hong.Children.Count; i++)
{
  Console.WriteLine(format: &quot; {0}'의 {1}번째 자녀 : \&quot;{2}\&quot;.&quot;, arg0: hong.Name, arg1: i, arg2: hong.Children[i].Name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PFHhu/btsEeIyXXu2/yHKbf4xtseZJyuaKZ90i6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PFHhu/btsEeIyXXu2/yHKbf4xtseZJyuaKZ90i6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PFHhu/btsEeIyXXu2/yHKbf4xtseZJyuaKZ90i6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPFHhu%2FbtsEeIyXXu2%2FyHKbf4xtseZJyuaKZ90i6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;360&quot; height=&quot;214&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보는바와 같이 동일한 개체 type에 대한 2개의 intance에 적용되는 기능의 경우 이에 필요한 정적 method와 intance method를 모두 제공하여 동일한 기능을 구현하고 있습니다. 정적 method든 intance method든 모든 경우에 적합한 method는 없으며 특정 type이 어떻게 사용될지 예측할 수도 없습니다. 따라서 두 method를 모두 제공하여 개발자가 원하는 방식으로 type을 적절히 사용하도록 하는 것이 가장 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밑에서 부터는 이제 type에 대한 두 intance에 대해 어떠한 방식으로 위와 동일한 기능을 부여할 수 있는지 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 연산자를 사용한 기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.String은 2개의 문자열을 연결하고 그 결과를 반환하는 Concat이라는 정적 method를 갖고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706662181001&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s1 = &quot;Hello &quot;; 
string s2 = &quot;World!&quot;;
string s3 = string.Concat(s1, s2); 
Console.WriteLine(s3);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Concat method를 통해서도 가능하지만 사실 개발자에게는 + 연산자를 통해 두 문자열을 결합하는 것이 더 자연스러울 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706662340985&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s3 = s1 + s2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 우리는 +연산자를 통해 둘의 People에 대한 결혼식을 올리고 *연산자로 자식을 낳는 동작을 수행할 수 있습니다. 연산자는 method이름대신 기호를 사용한다는 것만 다를뿐 하나의 method이므로 문법역시 method와 동일하게 구현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 아래와 같이 정적 method를 추가하여 결혼을 구현하고&lt;/p&gt;
&lt;pre id=&quot;code_1706662631066&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static bool operator +(Person p1, Person p2)
{
	Marry(p1, p2);
 	return p1.Married &amp;amp;&amp;amp; p2.Married;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;연산자에 대한 반환 type은 매개변수로 전달된 type과 일치할 필요는 없지만 void가 될 수는 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 &amp;times;에 대한 정적 method로 출산을 구현합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706662967417&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Person operator *(Person p1, Person p2)
{
  return Procreate(p1, p2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method와 달리 연산자는 intellisense에 나타나지 않습니다. 개발자의 입장에서 연산자를 사용할 수 있는지가 불분명하기 때문에 그에 대응하는 method를 같이 만들어두는것이 좋습니다. 이때 연산자를 구현하는것 안에서 method를 호출하도록 하면 code를 재사용하는 것이 가능할 것입니다. Method를 구현해야 하는 두번째 이유로는 모든 언어에서 연산자를 지원하지는 않기 때문입니다. 즉, 다른 언어에서 C#에서 지원하는 모든 연산자를 지원한다는 보장이 없는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 정의한 method는 다음과 같은 방법으로 호출됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706663761443&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person hong = new() { Name = &quot;홍길동&quot; };
Person kim = new() { Name = &quot;김길순&quot; };
//Person lee = new() { Name = &quot;이길영&quot; };

if (hong + kim)
{
  Console.WriteLine($&quot;{hong.Name}과 {kim.Name}양의 혼인이 성사됨.&quot;);
}

Person baby = hong * kim;
baby.Name = &quot;홍아기&quot;;

kim.WriteChildrenToConsole();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;35&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwnbHC/btsEdBN7zJy/lI8U3AENxzrQupHdbmIiO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwnbHC/btsEdBN7zJy/lI8U3AENxzrQupHdbmIiO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwnbHC/btsEdBN7zJy/lI8U3AENxzrQupHdbmIiO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwnbHC%2FbtsEdBN7zJy%2FlI8U3AENxzrQupHdbmIiO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;242&quot; height=&quot;35&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;35&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 link를 통해서는 더 많은 연산자의 재정의 대해 알아볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading&quot;&gt;Operator overloading - Define unary, arithmetic, equality, and comparison operators. - C# | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1706664011031&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Operator overloading - Define unary, arithmetic, equality, and comparison operators. - C#&quot; data-og-description=&quot;Learn how to overload a C# operator and which C# operators are overloadable. In general, the unary, arithmetic, equality and comparison operators are overloadable.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bd0XA8/hyVfY5xx6o/KjMa6GZu2FUkhXOSEeeZi1/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bd0XA8/hyVfY5xx6o/KjMa6GZu2FUkhXOSEeeZi1/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Operator overloading - Define unary, arithmetic, equality, and comparison operators. - C#&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to overload a C# operator and which C# operators are overloadable. In general, the unary, arithmetic, equality and comparison operators are overloadable.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. Generic을 통해 안전하게 재사용할 수 있는 type만들기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2005년 C# 2.0과 .NET Framework 2.0에서 Microsoft는 type을 좀 더 안전하게 재사용할 수 있으며 더 효율적으로 활용할 수 있는 generic이라는 기능을 도입했습니다. 이를 통해 개발자는 개체를 매개변수로 전달하는 방식과 비슷하게 type을 매개변수로 전달하여 generic이 제공하는 유연성을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 비 generic type 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 generic type을 사용하지 않는 경우에 먼저 살펴볼 것입니다. 이를 통해 취약하게 입력된 매개변수 및 값에서와 같은 문제와 System.Object의 사용으로 인한 성능문제를 해결하기 위해 generic이 어떤 개념으로 설계되었는지를 이해할 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Collections.Hashtable는 나중에 값을 빠르게 찾는 데 사용되는 각각의 고유한 key와 값을 다수로 저장하는 데 사용되며 key와 value모두 System.Object로 선언되었기에 object type이 될 수 있습니다. 비록 int와 같은 type값을 저장할 때 object사용에 대한 유연성을 제공해 주기는 하지만 generic보다 상대적으로 느릴 수밖에 없으며(boxing과 unboxing) item을 추가할 때 type을 확인하지 않으므로 bug가 발생되기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 비 generic collection인 System.Collections.Hashtable에 대한 intance를 생성하고 아래와 같이 4개의 item을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685692848230&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;System.Collections.Hashtable lookupObject = new();

lookupObject.Add(key: 1, value: &quot;one&quot;);
lookupObject.Add(key: 2, value: &quot;two&quot;);
lookupObject.Add(key: 3, value: &quot;three&quot;);
lookupObject.Add(key: kdh, value: &quot;Person&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 2의 값을 가진 key를 정의하고 이를 hashtable에서 값을 찾기 위한 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685692914516&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int key = 2;
Console.WriteLine(format: &quot;Key {0} has value: {1}&quot;, arg0: key, arg1: lookupObject[key]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 kdh개체를 사용해 값을 찾은 구문도 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685692977955&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(format: &quot;Key {0} has value: {1}&quot;, arg0: kdh, arg1: lookupObject[kdh]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행시키면 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dE8PwW/btsD6xessiA/Wvixji2vPbw4jkndGbKa9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dE8PwW/btsD6xessiA/Wvixji2vPbw4jkndGbKa9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dE8PwW/btsD6xessiA/Wvixji2vPbw4jkndGbKa9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdE8PwW%2FbtsD6xessiA%2FWvixji2vPbw4jkndGbKa9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;78&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 code는 잘 작동하긴 했지만 실질적으로 모든 type이 key와 값으로 사용될 수 있기 때문에 잠재적인 문제점을 가지고 있습니다. 다른 개발자가 예제의 lookupObject를 사용하고 모든 item이 int와 같은 특정 type이라고 예상한다면 아마도 해당 type으로 변환을 시도할 것입니다. 하지만 모든 type이 일관된 type은 아니므로 어느 지점에서는 예외를 일으킬 수 있습니다. 또한 lookupObject가 많은 item을 가지면 가질수록 성능은 저하됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;System.Collections namespace의 import를 되도록이면 피해야 합니다. 대신 System.Collections.Generics와 다른 namespace를 대신 import 하여 사용하는 것이 좋습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) generic type 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Collections.Generic.Dictionary&amp;lt;TKey, TValue&amp;gt;도 역시 System.Collections.Hashtable과 마찬가지로 값을 빠르게 검색하는 데 사용되는 각각의 고유한 key를 가진 다수의 값을 저장할 수 있고 이때 key와 값은 어떠한 개체도 될 수 있습니다. 다만 compiler에게 key와 값의 type으로 어떤 것을 사용할지를 collection을 instance화 할 때 명시해야 합니다. 이때 TKey와 TValue의 generic 매개변수를 통해 type을 특정할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;통상 generic type이 정의가능한 type을 하나 가질 때 이름은 T가 됩니다. 예를 들어 List&amp;lt;T&amp;gt;에서 T는 list에 저장되는 type을 말합니다. generic type이 정의가능한 type을 여러 개 가지는 경우에는 접두사로서의 이름이 T가 되어야 하고 그 뒤에 Dictionary&amp;lt;TKey, TValue&amp;gt;처럼 용도에 맞는 이름이 부여됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 충분한 유연성을 제공하며 빠르고, item이 추가될 때 type을 확인하게 되므로 bug 또한 최소화할 수 있습니다. Dictionary&amp;lt; TKey, TValue&amp;gt;를 포함하고 있는 System.Collections.Generic namespace는 기본적으로 전역 import 되므로 별도의 import를 지정할 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 generic을 사용하여 어떻게 문제를 해결할 수 있는지를 확인해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 generic lookup collection Dictionary&amp;lt;TKey, TValue&amp;gt;에 대한 instance를 생성하고 아래와 같이 4개의 item을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685753903627&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dictionary&amp;lt;int, string&amp;gt; lookupIntString = new();

lookupIntString.Add(key: 1, value: &quot;one&quot;);
lookupIntString.Add(key: 2, value: &quot;two&quot;);
lookupIntString.Add(key: 3, value: &quot;three&quot;);
lookupIntString.Add(key: kdh, value: &quot;person&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 compiler는 kdh에 대한 오류를 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SaDcK/btsEbyJ9SZP/AQugTTPn8Yt1zGpqNLpk21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SaDcK/btsEbyJ9SZP/AQugTTPn8Yt1zGpqNLpk21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SaDcK/btsEbyJ9SZP/AQugTTPn8Yt1zGpqNLpk21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSaDcK%2FbtsEbyJ9SZP%2FAQugTTPn8Yt1zGpqNLpk21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;171&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 kdh를 숫자 4로 바꾸고 key변수도 4로 설정한 뒤 아래와 같이 dictionary에서 값을 검색하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685754178234&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lookupIntString.Add(key: 4, value: &quot;person&quot;);

Console.WriteLine(format: &quot;Key {0} has value: {1}&quot;, arg0: key, arg1: lookupIntString[key]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo2Lym/btsD3LR3Xfb/z0ZHQwtcMQHfn0MlIyhtz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo2Lym/btsD3LR3Xfb/z0ZHQwtcMQHfn0MlIyhtz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo2Lym/btsD3LR3Xfb/z0ZHQwtcMQHfn0MlIyhtz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo2Lym%2FbtsD3LR3Xfb%2Fz0ZHQwtcMQHfn0MlIyhtz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. Event 발생과 제어&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method는 종종 개체가 그 자체로 또는 관련 개체에서 수행할 수 있는 동작으로 설명됩니다. 예를 들어 List&amp;lt;T&amp;gt;는 자체적으로 item을 추가할 수 있고 스스로 item을 비울 수 있으며 File은 filesytem안에서 file을 생성하고 삭제하는 동작을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Event는 개체로 발생되는 동작을 말합니다. 예를 들어 사용자 interface에서 button은 click event를 가지며 click는 button에서 발생하는 것입니다. FileSystemWatcher는 directory나 file에 대한 변화가 발생할 때 작동되는 Created 및 Deleted와 같은 event를 발생시키고 변경사항을 알리기 위해 filesystem을 감시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Event를 좀 다른 측면으로 보면 두 개체사이에 message를 교환하는 방법을 제공한다고도 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Event는 delegate를 내장하고 있는데 event에 대해 들어가 보려면 우선 이 delegate가 무엇인지 그리고 어떻게 작동하는지를 먼저 알아볼 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Delegate를 사용한 method 호출&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 예제를 통해서 우리는 이미 method를 호출하고 실행하는 가장 일반적인 방식을 사용해 봤습니다. .(점) 연산자를 통해 method의 이름을 사용하여 접근하는 것으로 예를 들어 Console.WriteLine는 Console type에 있는 WriteLine method를 호출한다는 의미가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method를 호출하고 실행하는 다른 방법으로는 delegate를 사용하는 것입니다. 함수 pointer를 지원하는 언어를 사용해 본 적이 있다면 delegate를 type-safe method pointer로 생각할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, delegate는 해당 delegate와 동일한 특성에 일치하는&amp;nbsp; method의 memory주소를 포함하고 있으므로 정확한 매개변수의 type을 통해 안전하게 호출할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, Person class에서 아래와 같이 매개변수에 string type을 전달되어야 하며 반환값의 type이 int인 method가 있다면&lt;/p&gt;
&lt;pre id=&quot;code_1685770360672&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public int MyMethod(string input)
{
	return input.Length;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person의 p1과 같은 instance를 통해 method를 아래와 같이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685770487183&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person p1 = new();
int result = p1.MyMethod(&quot;Frog&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 간접적으로 호출될 method의 특성과 일치하는 delegate를 정의할 수 있는데 이때 delegate는 매개변수의 이름과는 일치할 필요가 없이 단지 매개변의 type과 반환되는 값의 type만 일치하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685770701720&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;delegate int DelegateWithMatchingSignature(string s);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 호출하고자 하는 method의 point를 부여하고 아래와 같이 delegate의 instance를 생성한 뒤 method를 호출하는 것처럼 delegate를 호출하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685770988607&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DelegateWithMatchingSignature d = new(p1.MyMethod);
int result = d(&quot;Flog&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method를 호출하기에 다소 복잡한 과정을 거치는 것처럼 보이기 때문에 어쩌면 '이렇게 method를 호출할 필요가 있을까?'라는 질문을 던질 수도 있습니다. 결론부터 얘기하면 이것은 유연성을 제공하기 위한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 순서대로 호출되어야 하는 method의 queue를 생성하기 위해 delegates를 사용할 수도 있습니다. 향상된 확장성을 제공하기 위해 수행되어야 하는 queue의 action은 service에서 일반적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 예로는 다수의 action이 병렬적으로 동작되어야 하는 경우를 들 수 있습니다. Delegate는 다른 thread에서 동작하는 비동기 운영을 지원하기 위한 내장된 기능을 갖고 있으며 이로 인해 향상된 응답성을 제공할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 것은 delegate는 우리가 서로에 대해서 알지 못하는 다른 개체들 사이에 message를 전달하기 위한 event를 구현할 수 있도록 한다는 것입니다. Event는 서로에 대해 알 필요가 없는 component사이에서 느슨한 결합의 예가 될 수 있는데 이때는 그저 event의 특성만 알고 있으면 될 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Delegate 정의하고 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 event로 사용하기 위해 2개의 delegate를 사전 정의하였고 해당 delegate는 모두 아래 2개의 매개변수를 가지고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;object? sender : 해당 매개변수는 event를 발생시킨 혹은 message를 보낸 개체를 참조하며 null이 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;EventArgs e 또는 TEventArgs e : 해당 매개변수는 발생한 event에 대한 추가적인 관련 정보를 포함합니다. 예를 들어 GUI환경에서 MouseMoveEventArgs를 정의할 수 있는데 여기에는 mouse pointer가 위치한 x, y좌표를 나타내는 속성을 가지고 있습니다. 은행 계좌의 경우라면 출금하는 금액의 속성을 가진 WithdrawEventArgs를 가질 수도 있을 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들 delegate의 특성은 간단하지만 유연합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685859112552&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public delegate void EventHandler(object? sender, EventArgs e);
public delegate void EventHandler&amp;lt;TEventArgs&amp;gt;(object? sender, TEventArgs e);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 첫 번째는 추가적인 인수값을 전달할 필요가 없는 method용이며 두 번째는 generic type TEventArgs에 의해 정의된 대로 인수값을 전달해야 하는 method용입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자체 type으로의 event를 정의해야 하는 경우에는 이들 2개의 사전정의된 delegate 중 하나를 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 delegate와 event를 직접 사용해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person class에 아래와 같은 구문을 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1685859600272&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public EventHandler? Shout;

public int AngerLevel;

public void Poke()
{
	AngerLevel++;

	if (AngerLevel &amp;gt;= 3)
	{
		if (Shout != null)
		{
			Shout(this, EventArgs.Empty);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 사항에 주목합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제에서는 Shout이름의 EventHandler delegate field를 정의하였습니다.&lt;/li&gt;
&lt;li&gt;AngerLevel을 저장하기 위한 int field를 정의하였습니다.&lt;/li&gt;
&lt;li&gt;Poke이름의 method를 정의하였습니다.&lt;/li&gt;
&lt;li&gt;person에서는 Poke method가 실행될 때마다 AngerLevel field를 증가시키고 3의 값에 도달하면 Shout event를 발생시키도록 되어 있지만 단 적어도 단 하나의 event delegate가 code가 아닌 외부에서 정의된 method를 가라키고 있어야 합니다. 즉 null이 되어서는 안 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Method를 호출하기 전 개체가 null인지 아닌지를 확인하는 것은 아주 일반적인 것입니다. C# 6 이후부터는 .연산자전에 ?문자를 통해 inline에서 간소화된 null확인이 가능해졌습니다.&lt;br /&gt;
&lt;pre id=&quot;code_1685860401440&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shout?.Invoke(this, EventArgs.Empty);​&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyApp project에서는 Program.cs에서 sender 매개변수로 부터 Person개체를 참조하고 이들에 대한 일부 정보를 출력하는 특성과 일치하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685860658816&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Man_Shout(object? sender, EventArgs e)
{
	if (sender is null) return;

	Person? p = sender as Person;

	if (p is null) return;

	Console.WriteLine($&quot;{p.Name} is this angry: {p.AngerLevel}.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Event를 처리하기 위한 method의 이름은 Microsoft에서 관례적으로 '개체이름_Event이름'으로 지정하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 delegate field로 method를 할당하고 네 번에 걸쳐 Poke method를 호출하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685861325017&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kdh.Shout = Man_Shout;
kdh.Poke();
kdh.Poke();
kdh.Poke();
kdh.Poke();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다. Poke method를 호출하는 처음 두 번은 아무런 반응도 없다가 최소 3번의 Poke가 실행되면 Man_Shout event를 발생시킬 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9qIdy/btsEaQkgXxj/DOXO4PiEpLxScN34KAd8ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9qIdy/btsEaQkgXxj/DOXO4PiEpLxScN34KAd8ZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9qIdy/btsEaQkgXxj/DOXO4PiEpLxScN34KAd8ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9qIdy%2FbtsEaQkgXxj%2FDOXO4PiEpLxScN34KAd8ZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;115&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Event 정의하고 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정을 통해 event의 기능적인 면에서 가장 중요한 delegate를 어떻게 구현할 수 있는지를 알아보았습니다. 이를 통해 code의 완전히 다른 조각으로 구현되어야 하는 method의 특성을 정의하고 해당 method와 그 외 delegate field와 연결된 다른 모든 것들을 호출합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Event는 위에서 알아본 것과는 비교적 알아야 할 내용이 많지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Delegate field로 method를 할당할 때는 이전 예제에서처럼 단순한 할당 연산자를 통해서 할당하는 것은 피해야 합니다. =연산자 대신 +=연산자를 사용함으로써 더 많은 method를 delegate field에 할당할 수 있습니다. Delegate가 호출되면 비록 순서자체를 제어할 수는 없지만 여기에 할당된 모든 method가 호출될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shout delegate field가 이미 하나 또는 그 이상의 method를 참조하고 있을 때 =연산자를 통해 새로운 method를 할당하게 되면 다른 모든 것들을 대체하게 됩니다. 따라서 Event를 위해 사용되는 delegate에서는 일반적으로 method의 추가와 삭제를 위해 개발자가 +=이나 -=연산자를 사용하도록 강제되곤 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 event keyword를 선언된 delegate field에 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685863292775&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public event EventHandler? Shout;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 기존에 Program.cs에서 event를 할당하던 구문에는 다음과 같은 compiler error message가 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VRYOz/btsD6xrY9lQ/P0nFzgDHkqsCOo6Ckzk8KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VRYOz/btsD6xrY9lQ/P0nFzgDHkqsCOo6Ckzk8KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VRYOz/btsD6xrY9lQ/P0nFzgDHkqsCOo6Ckzk8KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVRYOz%2FbtsD6xrY9lQ%2FP0nFzgDHkqsCOo6Ckzk8KK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;785&quot; height=&quot;92&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event keyword가 하는 역할의 대부분이 바로 이것입니다. 만약 delegate field에 하나이상의 method가 할당되기를 원하지 않는다면 기술적으로는 event keyword가 필요하지 않지만 delegate field에 event가 사용된다는 것을 대부분 예상할 수 있기 때문에 기본적으로 event keyword를 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기본 method할당 문을 +=연산자로 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685863721879&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kdh.Shout += Man_Shout;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 동일한 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 기존 Man_Shout method를 복사해 method의 이름과 WriteLine method를 아래와 같이 변경해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685863853391&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Man_Shout2(object? sender, EventArgs e)
{
	if (sender is null) return;

	Person? p = sender as Person;

	if (p is null) return;

	Console.WriteLine($&quot;Stop it!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 기존 Man_Shout method를 할당한 구문 다음에 위 새로운 method를 할당하는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685863927367&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kdh.Shout += Man_Shout;
kdh.Shout += Man_Shout2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 다시 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ocMco/btsEcaoKGhC/IkrQJTE0Qq3XanriqGIxoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ocMco/btsEcaoKGhC/IkrQJTE0Qq3XanriqGIxoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ocMco/btsEcaoKGhC/IkrQJTE0Qq3XanriqGIxoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FocMco%2FbtsEcaoKGhC%2FIkrQJTE0Qq3XanriqGIxoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;134&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. Interface의 구현&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Interface는 표준적인 기능을 구현하고 새로운 것을 생성하기 위해 다른 type을 연결하는 방법을 제공하는데 LEGO brick에서 서로 같이 붙일 수 있는 Studs on top이나 plug, socket의 전기적 표준과 같은 것으로 생각할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Interface에 대한 type을 구현하면 특정한 기능을 구현한다는 약속을 만들게 되는는데 이러한 특징 때문에 종종 interface를 계약으로 설명하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 공통 interface&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 type에서 구현할 수 있는 공통 interface를 나타낸 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.7054%;&quot;&gt;Interface&lt;/td&gt;
&lt;td style=&quot;width: 27.2868%;&quot;&gt;Method&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.7054%;&quot;&gt;IComparable&lt;/td&gt;
&lt;td style=&quot;width: 27.2868%;&quot;&gt;CompareTo(other)&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%;&quot;&gt;type이 instance를 정렬하기 위한 비교 method를 정의합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.7054%;&quot;&gt;IComparer&lt;/td&gt;
&lt;td style=&quot;width: 27.2868%;&quot;&gt;Compare(first, second)&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%;&quot;&gt;second type이 first type의 instance를 정렬하기 위한 비교 method를 정의합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.7054%;&quot;&gt;IDisposable&lt;/td&gt;
&lt;td style=&quot;width: 27.2868%;&quot;&gt;Dispose()&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%;&quot;&gt;finalizer를 기다리는 것보다 더 효휼적으로 비관리 resource를 해제하기 위한 소거 method를 정의합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.7054%;&quot;&gt;IFormattable&lt;/td&gt;
&lt;td style=&quot;width: 27.2868%;&quot;&gt;ToString(format, culture)&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%;&quot;&gt;개체의 값을 문자열로 표현할때 이를 형식화 하기 위한 문화권 인식 method를 정의합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.7054%;&quot;&gt;IFormatter&lt;/td&gt;
&lt;td style=&quot;width: 27.2868%;&quot;&gt;Serialize(stream, object)&lt;br /&gt;Deserialize(stream)&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%;&quot;&gt;저장 또는 전송을 위해 개체를 byte stream또는 byte strem에서 개체로 변환하는 method를 정의합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.7054%;&quot;&gt;IFormatProvider&lt;/td&gt;
&lt;td style=&quot;width: 27.2868%;&quot;&gt;GetFormat(type)&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%;&quot;&gt;언어와 지역에 기반해 입력을 형식화 하는 method를 정의합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 정렬 시 개체의 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 구현되는 가장 일반적인 interface 중 하나는 IComparable입니다. 해당 interface는 CompareTo이라는 method하나를 정의하고 있는데 2가지 형태로 사용될 수 있습니다. 하나는 null가능한 개체 type을 필요로 하며 다른 하나는 null가능한 generic type T를 필요로 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685958794708&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace System
{
	public interface IComparable
	{
		int CompareTo(object? obj);
	}
	public interface IComparable&amp;lt;in T&amp;gt;
	{
		int CompareTo(T? other);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 string type에서는 string이 비교되기 전에 정렬되어야 한다면 -1을, 그 후에 정렬되어야 한다면 1을, 동등하다면 0을 반환하는 IComparable를 구현하고 있습니다. int type에서는 비교되는 int type보다 더 작으면 -1을, 더 크면 1을, 동등하다면 0을 반환하는 IComparable을 구현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type이 IComparable interface중 하나를 구현한다면 type의 instance를 포함하는 array와 collection은 정렬될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IComparable interface와 Person class의 CompareTo method를 구현하기 전에 Name속성에 null값을 가진 일부를 포함하고 있는 Person instance의 array를 정렬할 때 어떠한 동작이 실행되는지를 확인해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyApp project의 Program.cs에서&amp;nbsp;title과 함께 매개변수로 전달된 people collection에서 이름을 출력할 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685961951291&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputPeopleNames(IEnumerable&amp;lt;Person?&amp;gt; people, string title)
{
	Console.WriteLine(title);

	foreach (Person? p in people)
	{
		Console.WriteLine(&quot; {0}&quot;, p is null ? &quot;&amp;lt;null&amp;gt; Person&quot; : p.Name ?? &quot;&amp;lt;null&amp;gt; Name&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Person instance의 array를 생성하고 console에 item을 출력하기 위해 OutputPeopleNames method를 호출하는 문을 추가합니다. 그리고 array를 정렬한 다음 console로 item을 다시 출력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685961971795&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person?[] people =
{
	null,
	new() { Name = &quot;kim&quot; },
	new() { Name = &quot;hong&quot; },
	new() { Name = &quot;ahn&quot; },
	new() { Name = null },
	new() { Name = &quot;lee&quot; }
};

OutputPeopleNames(people, &quot;정렬 전 people&quot;);

Array.Sort(people);

OutputPeopleNames(people, &quot;Person의 IComparable implementation을 사용해 정렬한 후&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 예외를 발생시키게 됩니다. 예외 message에서는 주어진 문제를 해결하기 위해 type이 IComparable를 구현해야 함을 말하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o084A/btsEbFWPob9/AmwvrIppIBLlMMJPpYJn01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o084A/btsEbFWPob9/AmwvrIppIBLlMMJPpYJn01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o084A/btsEbFWPob9/AmwvrIppIBLlMMJPpYJn01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo084A%2FbtsEbFWPob9%2FAmwvrIppIBLlMMJPpYJn01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;381&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 아래와 같이 IComparable&amp;lt;Person?&amp;gt;을 상속하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685962139315&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person : IComparable&amp;lt;Person?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 Visual Studio에서는 추가한 code아래에 붉은색 밑줄을 긋고 필요한 method가 아직 구현되지 않았음을 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YYa1u/btsD6xlcD30/1zUp8EpJYksY0LkpK7HiuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YYa1u/btsD6xlcD30/1zUp8EpJYksY0LkpK7HiuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YYa1u/btsD6xlcD30/1zUp8EpJYksY0LkpK7HiuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYYa1u%2FbtsD6xlcD30%2F1zUp8EpJYksY0LkpK7HiuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;766&quot; height=&quot;131&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 왼쪽 전구모양을 click 하고 'Implement interface'를 click 하면 method의 기본 틀을 자동으로 생성할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zjcE6/btsD3Keykc2/Fk3eAAPKdJ6nfNqFkzPvhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zjcE6/btsD3Keykc2/Fk3eAAPKdJ6nfNqFkzPvhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zjcE6/btsD3Keykc2/Fk3eAAPKdJ6nfNqFkzPvhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzjcE6%2FbtsD3Keykc2%2FFk3eAAPKdJ6nfNqFkzPvhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;306&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1685962420035&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public int CompareTo(Person? other)
{
	throw new NotImplementedException();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method안에서는 NotImplementedException throw부분을 제거하고 null포함해 입력값의 다양성을 처리하는 문을 추가한 뒤 Name field에 대한 CompareTo method를 호출합니다. 이를 통해 string type의 CompareTo구현을 사용하여 결과를 반환하게 될 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685962655804&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public int CompareTo(Person? other)
{
	int position;
	if ((this is not null) &amp;amp;&amp;amp; (other is not null))
	{
		if ((Name is not null) &amp;amp;&amp;amp; (other.Name is not null))
		{
			position = Name.CompareTo(other.Name);
		}
		else if ((Name is not null) &amp;amp;&amp;amp; (other.Name is null))
		{
			position = -1;
		}
		else if ((Name is null) &amp;amp;&amp;amp; (other.Name is not null))
		{
			position = 1;
		}
		else
		{
			position = 0;
		}
	}
	else if ((this is not null) &amp;amp;&amp;amp; (other is null))
	{
		position = -1;
	}
	else if ((this is null) &amp;amp;&amp;amp; (other is not null))
	{
		position = 1;
	}
	else
	{
		position = 0;
	}
    
	return position;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서는 Name field를 비교하여 두 Person instance를 비교하도록 선택했습니다. 그러므로 Person instance는 자신의 Name에 의해 alphabet순으로 정렬될 것입니다. 이때 null은 collection의 아래로 정렬됩니다. 따라서 계산된 위치를 반환하기 전에 저장하는 것이 debugging시에 유용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이번에는 Name이 alphabet순으로 정렬됨으로써 제대로 작동한다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DBGfT/btsD4ktmxUz/dT6y2n6JVHKHIWVU43CSd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DBGfT/btsD4ktmxUz/dT6y2n6JVHKHIWVU43CSd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DBGfT/btsD4ktmxUz/dT6y2n6JVHKHIWVU43CSd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDBGfT%2FbtsD4ktmxUz%2FdT6y2n6JVHKHIWVU43CSd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;324&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 자체적으로 생성한 type에 대해 정렬을 허용하고자 한다면 IComparable interface를 구현함으로써 이를 실현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Separate class를 사용한 개체의 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 경우에는 type에 대한 source code에 접근할 수 없고 따라서 IComparable interface를 구현할 수 없는 경우가 있을지도 모릅니다. 하지만 type에 대한 정렬을 수행하는 다른 방법이 존재하는데 약간 다른 interface인 IComparer를 구현하는&amp;nbsp; separate type을 생성하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyApp project에서 Comparer.cs이름의 새로운 class file을 추가하고 두 개의 Person instance를 비교할 IComparer interface를 구현하는 class를 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686024913343&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PersonComparer : IComparer&amp;lt;Person?&amp;gt;
{
	public int Compare(Person? x, Person? y)
	{
		int position;

		if ((x is not null) &amp;amp;&amp;amp; (y is not null))
		{
			if ((x.Name is not null) &amp;amp;&amp;amp; (y.Name is not null))
			{
				int result = x.Name.Length.CompareTo(y.Name.Length);

				if (result == 0)
				{
					return x.Name.CompareTo(y.Name);
				}
				else
				{
					position = result;
				}
			}
			else if ((x.Name is not null) &amp;amp;&amp;amp; (y.Name is null))
			{
				position = -1;
			}
			else if ((x.Name is null) &amp;amp;&amp;amp; (y.Name is not null))
			{
				position = 1;
			}
			else
			{
				position = 0;
			}
		}
		else if ((x is not null) &amp;amp;&amp;amp; (y is null))
		{
			position = -1;
		}
		else if ((x is null) &amp;amp;&amp;amp; (y is not null))
		{
			position = 1;
		}
		else
		{
			position = 0;
		}

		return position;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 위 예제의 class를 이용해 array를 정렬하는 문을 이전 예제에서 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686025069645&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Array.Sort(people, new PersonComparer());

OutputPeopleNames(people, &quot;Person의 IComparer implementation을 사용해 정렬한 후&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 Name길이와 alphabet순으로 정렬되는 다음의 결과를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmyrXQ/btsEa6mOPv2/vOf7uVXonSpM1wVFy8mwh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmyrXQ/btsEa6mOPv2/vOf7uVXonSpM1wVFy8mwh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmyrXQ/btsEa6mOPv2/vOf7uVXonSpM1wVFy8mwh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmyrXQ%2FbtsEa6mOPv2%2FvOf7uVXonSpM1wVFy8mwh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;324&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;people에 대한 array의 정렬을 수행할 때 예제에서는 PersonComparer type을 정렬 algorithm으로 대신사용할 것을 명시적으로 요청하였으므로 people은 kim과 같이 짧은 이름이 우선순위로 올라오게 되며 가장 긴 이름의 hong이 후순위로 정렬되는 것을 알 수 있습니다. 만약 2개 또는 그 이상으로 이름의 길이가 동등한 경우 이때는 ahn, kim과 같이 alphabet순의 정렬이 이루어지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 암시적 그리고 명시적인 interface구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Interface는 암시적으로 그리고 명시적인 방법으로 구현될 수 있습니다. 암시적인 구현은 간단하며 더 일반적이기도 합니다. 명시적인 구현은 type의 method가 같은 이름과 특성을 가진 여러 version을 가지고 있을 때 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 IGamePlayer와 IKeyHolder는 game에서 패할 수 있고 key에서는 key를 잃을 수 있으므로 둘 다 같은 매개변수를 가진 Lose라는 method를 가질 수 있습니다. type안에서 이 둘의 interface를 구현해야 할 때 단지 하나의 Lose구현만 암시적인 method가 될 수 있습니다. 둘의 interface가 같은 구현을 공유할 수 있다면 그렇게 되겠지만 그렇지 않다면 다른 Lose method는 다르게 구현되어야 하며 명시적으로 호출되어야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686030400189&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface IGamePlayer
{
	void Lose();
}
public interface IKeyHolder
{
	void Lose();
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1686030417100&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person : IComparable&amp;lt;Person?&amp;gt;, IGamePlayer, IKeyHolder
{
	//암시적인 구현
	public void Lose()
	{
	}

	//명시적인 구현
	void IGamePlayer.Lose()
	{
	}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1686030503315&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person p = new();
p.Lose(); // 임시적 호출

((IGamePlayer)p).Lose(); // 명시적 호출

//명시적인 호출의 또 다른 방법
IGamePlayer player = p as IGamePlayer;
player.Lose();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 기본 구현을 통한 interface 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 8에서 부터 interface를 위해 도입된 기능으로 기본 구현(default implementations)이라는 것이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyLibrary project에서 IPlayable.cs이름의 file을 추가하고 Play와 Pause라는 2개의 method를 가진 public IPlayable interface를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686032977227&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface IPlayable
{
	void Play();
	void Pause();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyApp project에서는 DvdPlayer.cs이름의 class file을 추가하고 IPlayable interface를 아래와 같이 구현합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686033095803&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class DvdPlayer : IPlayable
{
	public void Pause()
	{
		Console.WriteLine(&quot;DVD player is pausing.&quot;);
	}
	public void Play()
	{
		Console.WriteLine(&quot;DVD player is playing.&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상황에서 C# 8 이전에 Stop이라는 새로운 method를 추가해야 한다면 적어도 하나의 type이 본래 interface에서 구현될 수 있으므로 이렇게 하는 것은 불가능합니다. interface의 주요 기능중 하나는 성립된 계약을 지키게끔 하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 8에서 부터는 release이후에도 기본구현이 추가되면 이를 통해 새로운 member의 추가를 허용하게 되었습니다. C#을 사용하는 일부 개발자들은 사실 이러한 생각을 별로 좋아하지는 않습니다만 변경사항으로 인한 부작용이나 전체 interface를 새로 정의해야 하는 현실적인 이유로서 유용할 수 있으며 Java나 Swift 같은 다른 언어의 경우에는 비슷한 기술을 이미 허용하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 interface 구현은 기본 platform을 일부 근본적으로 바꾸어야 하기 때문에 .NET 5.0 또는 그 상위의 C#에서만 지원하고 있습니다. 그외 .NET Core, ..NET Standard, .NET Framework등에서는 지원하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPlayable interface에서 아래와 같이 기본 구현을 통해 Stop method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686034157603&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface IPlayable
{
	void Play();
	void Pause();
	void Stop() // default interface implementation
	{
		Console.WriteLine(&quot;Default implementation of Stop.&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 MyApp project를 build 하면 DvdPlayer가 Stop method를 구현하지 않았음에도 불구하고 정상적으로 이루어짐을 확인할 수 있습니다. 일단 이렇게 해놓으면 추후에 DvdPlayer에서 Stop에 대한 기본구현을 재정의함으로써 method를 구현할 수 있는 여지가 생깁니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. 참조와 값 type에서의 memory관리&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 간단하게만 참조 type에 대해서 언급해 왔는데 이제 이에 대해 좀 더 구체적으로 들어가 보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Memory의 범주에는 크게 2가지로 나누어 볼 수 있는데 하나는 stack 다른 하나는 heap입니다. 요즘 운영체제에서 stack과 heap은 물리적 혹은 가상 memory 어느 곳이든 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack memory는 CPU에 의해 직접적으로 관리되며 후입-선출 체계를 사용하고 L1과 L2 cache에서 data가 다뤄질 가능성이 많기 때문에 성능면에서 더 빠르게 작동합니다. heap memory가 느리긴 하지만 더 풍부한 용량을 가질 수 있는 반면 stack은 크기가 제한적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARM64, x86, x64에서 동작하는 Windows에서는 stack의 기본 size가 1MB이며 일반적인 Linux기반 운영체제에서는 8MB입니다. MAC OS의 terminal에서 ulimit -a 명령을 사용하면 stack의 크기가 8,192KB로 제한되어 있고 다른 memory의 경우에는 'unlimited'임을 알 수 있습니다. Stack memory가 쉽게 채워지고 이어서 'stack overflow'를 일으키기가 쉬운 이유가 바로 이러한 stack memory의 size제한 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 참조와 값 type의 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서는 개체의 type을 정의하기 위한 3가지 keyword가 존재합니다. class, record 그리고 struct가 그것입니다. 이 모든 것은 field나 method와 같은 member를 가질 수 있지만 다른 차이점은 이들이 어떻게 memory에 할당되느냐 하는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;record나 class를 통해 type을 정의하면 이것은 참조(reference) type이 됩니다. 즉, 개체 자체는 heap memory에 할당되며 해당 개체에 대한 heap의 주소만 stack에 저장됩니다. 이는 미약하지만 아주 약간의 overhead를 일으킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;record struct 혹은 struct를 사용해 type을 정의하면 이것은 값 type이 됩니다. 특, 개체 자체에 대한 memory가 stack에 그대로 할당됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Struct에서도 struct type이 아닌 field type을 사용하게 되면 해당 field는 heap에 저장될 것입니다. 따라서 이러한 개체의 data는 stack과 heap에 동시에 존재하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 일반적인 struct type은 아래와 같이 분류할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Number type : byte, sbyte, short, ushort, int, uint, long, ulong, float, double, decimal&lt;/li&gt;
&lt;li&gt;System.Drawing type : Color, Point, PointF, Size, SizeF, Rectangle, RectangleF&lt;/li&gt;
&lt;li&gt;기타 : char, DateTime, DateOnly, TimeOnly, bool&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.String(string)과 System.Object(object)를 포함에 다른 모든 type은 class입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Type의 data가 저장되는 memory의 장소에 대한 용어적인 차이점과는 별개로 가장 주된 차이점은 struct로부터는 상속이 불가능하다는 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 참조와 값 type이 memory에 저장되는 방식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같이 몇 개의 변수를 선언하는 console app이 있다고 가정했을 때&lt;/p&gt;
&lt;pre id=&quot;code_1686120732890&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int number1 = 49;
long number2 = 12;

System.Drawing.Point location = new(x: 4, y: 5);

Person kim = new()
{
	Name = &quot;kim hee seoung&quot;,
	DateOfBirth = new(year: 1982, month: 12, day: 13)
};

Person hong;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 실제 예제의 문이 실행될 때 어떤 것이 stack과 heap에 할당되는지 검토해 보면 아래와 같은 사항을 확인할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;number1 변수는 값 type(또는 struct)이므로 stack에 할당되며 32bit integer이므로 4byte의 memory를 사용하게 됩니다. 값은 49인데 이는 변수에 직접적으로 저장됩니다.&lt;/li&gt;
&lt;li&gt;number2 변수 역시 값 type이므로 stack에 할당되며 64bit integer이므로 8byte의 memory를 사용하게 됩니다.&lt;/li&gt;
&lt;li&gt;location 역시 값 type이며 stack에 할당됩니다. 또한 x와 y라는 2개의 32bit-integer로 이루어져 있으므로 8byte의 memory를 사용합니다.&lt;/li&gt;
&lt;li&gt;kim은 참조 type(class)이며 64bit 운영체제의 경우 64bit memory 주소를 위해 stack에 8byte가 할당됩니다. 그리고 Person의 instance를 저장하기 위해 충분한 공간의 heap이 할당됩니다.&lt;/li&gt;
&lt;li&gt;hong은 참조 type이며 stack에 64bit memory 주소를 저장하기 위해 8byte 공간을 사용하게 됩니다. 다만 현재 시점에서는 null이므로 아직 heap에 memory가 할당되지 않았습니다. 만약 예제에서 kim을 hong에 할당하게 된다면 heap에 대한 Person의 memory 주소가 hong으로 복사될 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRFhV7/btsEa6AoL3N/eddW2GBz1szhPVY0io9E51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRFhV7/btsEa6AoL3N/eddW2GBz1szhPVY0io9E51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRFhV7/btsEa6AoL3N/eddW2GBz1szhPVY0io9E51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRFhV7%2FbtsEa6AoL3N%2FeddW2GBz1szhPVY0io9E51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;468&quot; height=&quot;567&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 type에 할당된 모든 memory는 heap에 저장됩니다. 만약 Person과 같은 참조 type에서 field가 DateTime과 같은 값 type이라면 DateTime의 값 역시 heap에 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 type이 참조 type인 field를 가진 경우라면 값 type의 값은 heap에 저장됩니다. Point는 2개의 field로 이루어진 값 type이며 2개의 field모두 값 type이기 때문에 전체 개체는 stack에 할당될 수 있습니다. 만약 point 값 type이 string과 같이 참조 type인 field를 가지게 된다면 string byte는 heap에 저장될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Boxing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 boxing은 값 type이 heap memory로 옮겨지고 System.Object instance안으로 감싸는 것을 말합니다. 반대로 Unboxing은 값이 stack으로 되돌아 오는 경우를 의미하며 이때 Unboxing은 명시적으로 이루어집니다. boxing은 암시적으로 발생할 수 있으므로 개발자의 의식없이 이루어질 수 있고 이때 약간의 과부하가 생기게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 예제에서는 int값을 boxing하고 바로 다시 unboxing하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706830898809&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int n = 3;
object o = n; // 암시적인 Boxing
n = (int)o; // 명시적인 Unboxing&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 예로는 아래와 같이 값 type을 형식화된 문자열에 전달하는 경우입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706831076937&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s = &quot;hello&quot;;
int i = 10;

Console.WriteLine(&quot;stirng output {0} - {1}&quot;, s, i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 i는 값 type으로서 boxing이 이루어집니다. 문자열 s는 참조 type이고 이미 heap이 존재하므로 boxing/unboxing과는 관련이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상술했듯이 Boxing과 unboxing은 성능이 부정적인 영향을 줄 수 있으므로 개발자 입장에서는 되도록 이러한 동작이 발생하지 않게 하는 것이 좋지만 그렇다고 해서 너무 걱정할 필요는 없습니다. 실제 많은 code에서 boxing/unboxing은 자주 사용되며 너무 남발하지 않는 이상은 이것때문에 성능상 문제를 일으키는 경우가 거의 없는 경우가 많습니다. 물론 Unity와 같은 game개발의 경우에는 garbage collector가 기대한것 만큼 신속하게 동작하지 않을 수 있기 때문에 가능한한 이러한 동작이 발생되는 경우를 피하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Type의 동일성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 2개의 변수를 비교할 때는 ==이나 !=연산자를 사용하는데 이들 2개의 연산자는 참조와 값 type에서 다르게 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 값 type 변수에 대한 동일성을 확인할때 .NET은 실질적으로 stack에 있는 이들 2개 변수에 대한 값을 비교하고 동일한 값이라면 true를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 동일한 값을 가진 2개의 integer변수를 선언하고 이들을 비교하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686123890619&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int a = 3;
int b = 3;

Console.WriteLine($&quot;a: {a}, b: {b}&quot;);
Console.WriteLine($&quot;a == b: {(a == b)}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b32OJY/btsEbBmBuy8/whcmpe4YoKerqYOELiyHEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b32OJY/btsEbBmBuy8/whcmpe4YoKerqYOELiyHEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b32OJY/btsEbBmBuy8/whcmpe4YoKerqYOELiyHEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb32OJY%2FbtsEbBmBuy8%2Fwhcmpe4YoKerqYOELiyHEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;73&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 참조 type 변수에 대한 동일성을 확인할때 .NET은 이들 두 변수에 대한 memory 주소를 비교하고 동일하다면 true를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 둘의 Person instance를 선언하고 Name을 동일하게 설정한 뒤 이들 변수를 비교하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686124094091&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person p1 = new() { Name = &quot;Kim&quot; };
Person p2 = new() { Name = &quot;Kim&quot; };

Console.WriteLine($&quot;p1: {p1}, p2: {p2}&quot;);
Console.WriteLine($&quot;p1 == p2: {(p1 == p2)}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D5p0T/btsEbEKoeXC/JY1DeZmxHJwmEuyG9rim2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D5p0T/btsEbEKoeXC/JY1DeZmxHJwmEuyG9rim2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D5p0T/btsEbEKoeXC/JY1DeZmxHJwmEuyG9rim2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD5p0T%2FbtsEbEKoeXC%2FJY1DeZmxHJwmEuyG9rim2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;83&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 결과가 나오게 되는 이유는 개체가 다르기 때문입니다. 만약 이 둘의 변수가 실질적으로 heap상에 같은 개체를 가리키고 있다면 이들은 동일하게 판단될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 3번째 Person 개체를 선언하고 p1을 할당합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686124263243&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person p3 = p1;
Console.WriteLine($&quot;p3: {p3}&quot;);
Console.WriteLine($&quot;p1 == p3: {(p1 == p3)}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이번에는 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqoxyI/btsD8AhFQk2/vd7xxDvyaeNmGrFrYKtvVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqoxyI/btsD8AhFQk2/vd7xxDvyaeNmGrFrYKtvVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqoxyI/btsD8AhFQk2/vd7xxDvyaeNmGrFrYKtvVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqoxyI%2FbtsD8AhFQk2%2Fvd7xxDvyaeNmGrFrYKtvVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;83&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 type의 이러한 동작에 대한 한 가지 예외는 string type입니다. string은 참조 type이지만 동일성을 비교하는 연산자에서는 이들이 값 type인 것처럼 동작하도록 재정의되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 두 Person instance의 Name속성을 비교하는 문을 아래와 같이 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1686124490578&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;p1.Name: {p1.Name}, p2.Name: {p2.Name}&quot;);
Console.WriteLine($&quot;p1.Name == p2.Name: {(p1.Name == p2.Name)}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRY5wL/btsD4I1NrwL/jFum5bMjpP1UaPEKh6kSs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRY5wL/btsD4I1NrwL/jFum5bMjpP1UaPEKh6kSs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRY5wL/btsD4I1NrwL/jFum5bMjpP1UaPEKh6kSs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRY5wL%2FbtsD4I1NrwL%2FjFum5bMjpP1UaPEKh6kSs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;76&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 우리가 임의로 만든 class에 대해서도 서로가 같은 개체(heap상 같은 memory 주소)가 아니라도 해당 개체의 field가 같은 값을 가진 경우라면 비교 연산자가 true를 반환하도록 만들 수도 있습니다. 하지만 이렇게 하기보다는 record class를 사용하여 동일한 동작을 구현하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Struct type 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sturct를 통해서는 자체 값 type을 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyLibrary project에서 DisplacementVector.cs이름의 file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686182785716&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public struct DisplacementVector
{
	public int X { get; set; }
	public int Y { get; set; }

	public DisplacementVector(int initialX, int initialY)
	{
		X = initialX;
		Y = initialY;
	}

	public static DisplacementVector operator +(DisplacementVector vector1, DisplacementVector vector2)
	{
		return new(vector1.X + vector2.X, vector1.Y + vector2.Y);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 DisplacementVector에 대한 2개의 새로운 instance를 생성하고 이들에 대해 +연산을 수행한 뒤 그 결과를 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686182874725&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DisplacementVector dv1 = new(3, 5);
DisplacementVector dv2 = new(-2, 7);
DisplacementVector dv3 = dv1 + dv2;
Console.WriteLine($&quot;({dv1.X}, {dv1.Y}) + ({dv2.X}, {dv2.Y}) = ({dv3.X}, { dv3.Y})&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;64&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc5AYQ/btsD4SJYlf8/uqmrLMK9G4ZJSUfllzRnX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc5AYQ/btsD4SJYlf8/uqmrLMK9G4ZJSUfllzRnX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc5AYQ/btsD4SJYlf8/uqmrLMK9G4ZJSUfllzRnX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc5AYQ%2FbtsD4SJYlf8%2FuqmrLMK9G4ZJSUfllzRnX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;64&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;64&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 type에서는 기본 생성자를 명시적으로 정의하지 않는다 하더라도 stack상에 값이 초기화되어야 하므로 기본값을 통한 기본 생성자는 반드시 가지게 됩니다. 만약 이런 경우라면 DisplacementVector에 대한 2개의 integer field는 0으로 초기화될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 DisplacementVector에 대한 또 다른 instance를 하나 더 생성하고 해당 개체의 속성을 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686183149300&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DisplacementVector dv4 = new();
Console.WriteLine($&quot;({dv4.X}, {dv4.Y})&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vdUf4/btsEbzhZtEE/0OZFz95ZjxakKmz0qtX42k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vdUf4/btsEbzhZtEE/0OZFz95ZjxakKmz0qtX42k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vdUf4/btsEbzhZtEE/0OZFz95ZjxakKmz0qtX42k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvdUf4%2FbtsEbzhZtEE%2F0OZFz95ZjxakKmz0qtX42k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;73&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;type에 존재하는 모든 field에서 사용하는 최종 memory가 16byte이거나 혹은 그 보다 더 적고 이들 field가 값 type만을 사용하며 해당 type으로부터 파생되는 것이 필요하지 않다면 Microsoft에서는 struct를 사용할 것을 권장하고 있습니다. type에서 stack memory로 16byte 이상을 사용하고 field가 참조 type이거나 상속이 필요하다면 class를 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Record struct type 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 10에서는 class type에서 뿐만 아니라 struct type에서도 record keyword를 사용할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 DisplacementVector type은 아래와 같이 정의될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686183788891&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public record struct DisplacementVector(int X, int Y);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record struct는 class의 이점을 가진 struct인 record class와 모든 동일한 이점을 가집니다. record struct와 record class사이에 한 가지 차이점이라면 record struct는 readonly keyword를 적용하지 않는 이상은 불변이 아니라는 것입니다. struct는 ==와 !=연산자를 구현하지 않지만 자동적으로 record struct로 구현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 변화로 Microsoft는 record class를 정의하고자 할 때 class keyword가 선택 적라 하더라도 명시적으로 class를 지정해 줄 것을 권장하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686189764311&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public record class ImmutableAnimal(string Name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 비관리 resource 해제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 예제를 통해 우리는 생성자를 통해 field를 초기화할 수 있고 type은 여러 생성자를 가질 수 있음을 알게 되었습니다. 이때 만약 생성자가 OS의 제어하에 있는 file이나 mutex와 같은 .NET에서 관리되지 않는 비관리 resource를 할당할때 .NET은 자동적인 garbage collection기능을 사용해 비관리 resource를 해제할 수 없으므로 수동적인 해제처리가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 type은 resource의 해제가 필요할때 .NET runtime에서 호출될 수 있는 단일 finalizer를 가질 수 있습니다. Finalizer는 type의 이름인 생성자와 동일한 이름을 가지지만 앞에 접두사 ~문자를 가집니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686191576972&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ObjectWithUnmanagedResources
{
	public ObjectWithUnmanagedResources() // 생성자
	{
		// 비관리 resource 할당
	}
	~ObjectWithUnmanagedResources() // 종료자 (또는 파괴자)
	{
		// 비관리 resource 해제
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;finalizer(또는 destructor)를 Deconstruct method와 혼동해서는 안됩니다. destructor는 resource를 해제하는 것으로 개체를 memory에서 소거하는 것입니다. 하지만 Deconstruct method는 예를 들어 tuple을 사용할 때처럼 각각의 요소로 분해된 개체를 반환하며 C# deconstruction 문법을 사용합니다. 이에 대한 내용은 아래 글을 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12-%EA%B3%BC-NET-8-5-OOP-Object-Oriented-Programming&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[C# 12와 .NET 8] 5. OOP (Object-Oriented Programming)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1706853354263&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 5. OOP (Object-Oriented Programming)&quot; data-og-description=&quot;C#에서는 OOP를 통해 자신만의 type을 만들 수 있습니다. 이를 위해 data를 저장하기 위한 field와 동작을 수행하는 method를 포함해 type이 가질 수 있는 member들에 대해 encapsul화와 같은 OOP개념을 사용해&quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12-%EA%B3%BC-NET-8-5-OOP-Object-Oriented-Programming&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12-%EA%B3%BC-NET-8-5-OOP-Object-Oriented-Programming&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfm4wn/hyVgarIKY0/PgMvU2kUizzQriu6affk80/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/mRM0H/hyVb9aa1mH/3l7744Y6UrIWk8JUZejSM0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12-%EA%B3%BC-NET-8-5-OOP-Object-Oriented-Programming&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12-%EA%B3%BC-NET-8-5-OOP-Object-Oriented-Programming&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfm4wn/hyVgarIKY0/PgMvU2kUizzQriu6affk80/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/mRM0H/hyVb9aa1mH/3l7744Y6UrIWk8JUZejSM0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 5. OOP (Object-Oriented Programming)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;C#에서는 OOP를 통해 자신만의 type을 만들 수 있습니다. 이를 위해 data를 저장하기 위한 field와 동작을 수행하는 method를 포함해 type이 가질 수 있는 member들에 대해 encapsul화와 같은 OOP개념을 사용해&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 비관리 resource에 대한 처리를 할 수 있는 최소한의 구현이지만 finalizer를 제공함으로써 생길 수 있는 문제는. NET garbage collector가 type에 할당된 resource의 해제를 완료하기 위해 2번의 garbage collection 동작이 필요하게 된다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택적이기는 하지만 type을 사용하는 개발자가 명시적으로 resource를 해제할 수 있도록 하는 또 다른 method를 제공하는 것을 권장합니다. 이를 통해 garbage collector가 file과 같은 비관리 resource 중 관리되는 부분을 즉시 해제하도록 할 수 있습니다. 즉, garbage collection이 이중적인 실행대신 단일 garbage collection만으로 개체의 관리 memory 부분을 해제하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 아래와 같이 IDisposable interface를 통해 기본적인 mechanism을 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686192940386&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ObjectWithUnmanagedResources : IDisposable
{
	public ObjectWithUnmanagedResources()
	{
	}
	~ObjectWithUnmanagedResources()
	{
		Dispose(false);
	}

	bool disposed = false;
	public void Dispose()
	{
		Dispose(true);
		GC.SuppressFinalize(this);
	}

	protected virtual void Dispose(bool disposing)
	{
		if (disposed) return;

		if (disposing)
		{
		}

		disposed = true;

	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 public과 protected로 2개의 Dispose method가 존재하는데 public void Dispose method는 type을 사용하는 개발자에 의해 호출되는 것이며 비관리와 관리 resource를 할당 해제합니다. bool형식의 매개변수를 가진 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;protected virtual void&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Dispose method는 내부적으로 resource의 해제를 구현하기 위해 사용됩니다. 다만 여기서는 finalizer thread가 이미 동작중이고 ~ObjectWithUnmanagedResources method를 호출했다면 비관리 resource만이 할당해제가 필요한 상황이 되므로 disposing 매개변수와 disposed를 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC.SuppressFinalize(this)를 호출하는 것은 garbage collector에게 finalizer의 동작이 더 이상 필요하지 않음을 알려주기 위한 것이며 두 번째 garbage collection의 필요성을 제거합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Dispose의 호출을 보장하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 개발자가 IDisposable이 구현된 type을 사용할 때는 public Dispose method가 아래와 같은 문을 통해 호출되도록 해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686199497912&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using (ObjectWithUnmanagedResources thing = new())
{
	// ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문은 compiler가 아래와 같이 예외가 발생하는 경우에도 여전히 Dispose method가 호출되도록 code를 변환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686199593800&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ObjectWithUnmanagedResources thing = new();

try
{
	// ...
}
finally
{
	if (thing != null) thing.Dispose();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IAsyncDisposable을 구현한 type의 경우에는 아래와 같은 문을 통해 public Dispose method가 호출되도록 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686199668864&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await using (ObjectWithUnmanagedResources thing = new())
{
	// ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;7. null 사용&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 memory에 저장되는 방식에 따라 참조 type과 값 type이 어떻게 다른지와 struct변수에서 숫자와 같은 원시값이 저장되는 방식에 대해 알아보았습니다. 하지만 아직 변수가 값을 가지지 않았다면? 그런 경우라면 또 그것을 어떻게 표시할 수 있을까? C#은 이런 경우에 대해 null라는 개념을 제공하여 변수가 아직 값을 가지지 않았음을 표현하는 데 사용할 수 있도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 값 type을 null가능하게 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 int나 DateTime과 같은 값 type은 항상 값을 가져야 합니다. 그런데 null이나 빈 상태를 허용하는 database와 같은 곳에서 값을 읽을 때는 값 type에도 null을 허용하는 것이 편리할 수 있습니다. 우리는 이것을 'null가능한 값 type'이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이렇게 null을 허용하는 것은 변수를 선언할 때 type에 접미사로 ?문자를 사용함으로써 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy06 solution에 NullHandling이름의 Console App project를 추가하고 Program.cs에서 기존 구문을 모두 삭제한 뒤 int변수를 선언하고 null을 포함해 값을 할당하는 문을 추가합니다. 이때 하나는 ? 접미사를 사용하고 다른 하나는 사용하지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686206304709&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int thisCannotBeNull = 4;
thisCannotBeNull = null;
Console.WriteLine(thisCannotBeNull);

int? thisCouldBeNull = null;
Console.WriteLine(thisCouldBeNull);
Console.WriteLine(thisCouldBeNull.GetValueOrDefault());

thisCouldBeNull = 7;
Console.WriteLine(thisCouldBeNull);
Console.WriteLine(thisCouldBeNull.GetValueOrDefault());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 code를 작성하면 Visual Studio에서는 다음과 같은 compile error를 발생시키게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Brzk5/btsEaXKqbW3/DoaDr8By164KXWV6UhFvtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Brzk5/btsEaXKqbW3/DoaDr8By164KXWV6UhFvtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Brzk5/btsEaXKqbW3/DoaDr8By164KXWV6UhFvtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBrzk5%2FbtsEaXKqbW3%2FDoaDr8By164KXWV6UhFvtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;142&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;error가 발생한 부분의 code를 주석처리하고 예제를 실행하면 다음과 같은 결과가 표시될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;139&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lq2CP/btsD9BASja1/j2e18kQHBZkkJreMQ7zYjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lq2CP/btsD9BASja1/j2e18kQHBZkkJreMQ7zYjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lq2CP/btsD9BASja1/j2e18kQHBZkkJreMQ7zYjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flq2CP%2FbtsD9BASja1%2Fj2e18kQHBZkkJreMQ7zYjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;139&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;139&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 부분이 비어있는 이유는 null값이 표시된 것이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 아래와 같이 null을 지정하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686206652508&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Nullable&amp;lt;int&amp;gt; thisCouldAlsoBeNull = null;
thisCouldAlsoBeNull = 9;
Console.WriteLine(thisCouldAlsoBeNull);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Nullable&amp;lt;int&amp;gt;부분을 click 하고 F12 key를 누르거나 mouse 오른쪽 button을 눌러 'Go To Definition'을 선택하면 Nullable&amp;lt;T&amp;gt;유형인 generic 값 type은 값 type이라는 struct인 T type이 있어야 하며 HasValue, Value, GetValueOrDefaut와 같은 유용한 member가 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J5Qnq/btsEa94VNBM/vYIAy52OuAJncGHMwE9WN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J5Qnq/btsEa94VNBM/vYIAy52OuAJncGHMwE9WN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J5Qnq/btsEa94VNBM/vYIAy52OuAJncGHMwE9WN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ5Qnq%2FbtsEa94VNBM%2FvYIAy52OuAJncGHMwE9WN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;381&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;struct뒤에 ?를 붙이게 되면 다른 struct로 변경하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Null 관련 initial&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 null과 관련하여 일반적으로 사용되는 initial을 표시한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.44958%;&quot;&gt;Initial&lt;/td&gt;
&lt;td style=&quot;width: 21.5891%;&quot;&gt;의미&lt;/td&gt;
&lt;td style=&quot;width: 69.9612%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.44958%;&quot;&gt;NRT&lt;/td&gt;
&lt;td style=&quot;width: 21.5891%;&quot;&gt;Nullable reference types&lt;/td&gt;
&lt;td style=&quot;width: 69.9612%;&quot;&gt;C# 8과 함께 도입된 compiler기능이며 C# 10의 project에서 기본으로 활성화되어 design time에 code를 정적으로 분석하고 참조 type에 대한 null 값의 오용가능성을 경고표시합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.44958%;&quot;&gt;NRE&lt;/td&gt;
&lt;td style=&quot;width: 21.5891%;&quot;&gt;NullReferenceException&lt;/td&gt;
&lt;td style=&quot;width: 69.9612%;&quot;&gt;null 값을 역참조하려할때 runtime에서 발생하는 예외로, null값을 가진 변수나 member에 접근하는 경우를 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.44958%;&quot;&gt;ANE&lt;/td&gt;
&lt;td style=&quot;width: 21.5891%;&quot;&gt;ArgumentNullException&lt;/td&gt;
&lt;td style=&quot;width: 69.9612%;&quot;&gt;매개변수가 null이면서 null을 올바른 값으로 취급할 수 없을때 method가 호출됨으로서 runtime에서 발생하는 예외입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Null 가능 참조 type&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Null 값을 다루는 것은 여러 언어에서 아주 일반적인 것이라 많은 개발자들은 null존재에 대해서 별다른 의문을 가지고 있지는 않습니다. 그러나 변수가 null이 되는 것을 허용하지 않는다면 코드를 단순화할 수 있는 많은 상황이 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 8 compiler의 가장 중요한 변화중 하나는 null가능한 것과 null가능하지 않은 참조 type을 확인하고 경고를 표시한다는 것입니다. 그런데 참조 type은 이미 null이 가능한 type인데 왜 null여부를 확인하는 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 8부터 참조 type은 file 혹은 project 수준에서 option설정을 통해 더 이상 참조 type이 null이 가능하지 않도록 할 수 있습니다. 이것은 C#에서는 큰 변화에 해당하므로 Microsoft는 이 기능을 option으로 결정할 수 있게끔 만들어 두게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 C# compiler기능은 해당 기능이 적용되기 이전에 만들어진 것으로 예상되는 수천 가지의 library와 app에 적용되기까지는 몇 년의 시간이 걸릴 수도 있습니다. 심지어 Microsoft조차 .NET 6까지 주요 .NET package에 이 새로운 기능을 구현할 시간이 없었을 정도입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 변화하는 동안 우리는 우리가 만든 project에 위 기능과 관련된 몇 가지 접근 방식을 선택할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Default : .NET 5나 그 이전에 생성된 project에서는 어떠한 변경도 필요하지 않으며 null가능하지 않은 참조 type은 확인되지 않습니다. .NET 6나 이후에 생성된 project에서 null가능성 확인은 기본으로 사용되지만 csproj project file에서 &amp;lt;Nullable&amp;gt;요소를 삭제하거나 disable로 설정함으로써 해당 기능을 비활성으로 돌릴 수 있습니다.&lt;/li&gt;
&lt;li&gt;Opt-in project, opt-out files : project수준에서 해당 기능을 사용하고자 하는 것이며 예전 방식의 호환성으로 남아 있어야 하는 file들은 제외됩니다. 이것은 Microsoft가 새로운 기능을 사용하기 위해 자체 package가 update 될 때까지 내부적으로 사용하는 접근법입니다.&lt;/li&gt;
&lt;li&gt;Opt-in file: 설정한 각각의 file에서만 기능을 사용하도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) null가능성 경고 확인 기능 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project 수준에서 null가능한 경고 확인 기능을 사용하려면 csproj(project file)을 아래와 같이 설정해야 합니다. (반대로 기능을 사용하지 않으려면 disable로 맞춰줍니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1686291459989&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PropertyGroup&amp;gt;
...
 	&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File수준에서 기능을 사용하려면 아래와 같이 설정합니다.(반대로 기능을 사용하지 않으려면 disable로 맞춰줍니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1686291549902&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#nullable enable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) null 및 관련 / 기타 compiler 경고의 비활성처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project나 file수준에서 nullability기능을 사용하고자 하는 경우에도 이와 관련된 일부 경고 message는 나타나지 않도록 할 수 있습니다. 아래 표는 일반적으로 나타날 수 있는 nullability 경고를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 174px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.2791%; height: 20px;&quot;&gt;CS8600&lt;/td&gt;
&lt;td style=&quot;width: 83.7209%; height: 20px;&quot;&gt;null literal 또는 null가능값을 비 nullable type으로 변경합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.2791%; height: 20px;&quot;&gt;CS8601&lt;/td&gt;
&lt;td style=&quot;width: 83.7209%; height: 20px;&quot;&gt;가능한 null 참조 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.2791%; height: 20px;&quot;&gt;CS8602&lt;/td&gt;
&lt;td style=&quot;width: 83.7209%; height: 20px;&quot;&gt;가능한 null 참조의 역참조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.2791%; height: 20px;&quot;&gt;CS8603&lt;/td&gt;
&lt;td style=&quot;width: 83.7209%; height: 20px;&quot;&gt;가능한 null 참조의 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.2791%; height: 20px;&quot;&gt;CS8604&lt;/td&gt;
&lt;td style=&quot;width: 83.7209%; height: 20px;&quot;&gt;매개변수에 대한 가능한 null 참조 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 16.2791%; height: 40px;&quot;&gt;CS8618&lt;/td&gt;
&lt;td style=&quot;width: 83.7209%; height: 40px;&quot;&gt;생성자를 나올때 non-nullable field 'field 명'은 non-null value값을 포함해야 합니다. 필요하다면 field를 null가능한 type으로 선언합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.2791%; height: 17px;&quot;&gt;CS8625&lt;/td&gt;
&lt;td style=&quot;width: 83.7209%; height: 17px;&quot;&gt;null literal을 non-nullable 참조 type으로 변경할 수 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.2791%; height: 17px;&quot;&gt;CS8655&lt;/td&gt;
&lt;td style=&quot;width: 83.7209%; height: 17px;&quot;&gt;switch 표현식은 일부 null입력을 처리할 수 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 project단위에서 compiler 경고를 나타나지 않게 하려면 위와 같은 compiler 경고 code를 semi colon으로 구분한 NoWarn 요소를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707122042318&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;NoWarn&amp;gt;CS8600;CS8602&amp;lt;/NoWarn&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 경고 code를 임시적으로 제거했다가 다시 복구함으로서 구문단위로 특정한 경고의 발생을 제어할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707122522487&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma warning disable CS8602
...
...
#pragma warning restore CS8602&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) null일 수 없는 변수와 매개변수 선언&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null가능한 참조 type을 사용하고 참조 type에 null값을 할당하고자 한다면 null가능한 값 type을 표시하던 것과 같은 문법인 ?문자를 type선언뒤에 붙여주는 문을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 null가능한 참조 type은 어떻게 작동하는지 예제를 통해 알아보도록 하겠습니다. 주소에 대한 정보를 저장할 때 Street, City, Region에 대한 입력을 강제하고자 하지만 Building은 공백이 가능하다는 가정하에&amp;nbsp;NullHandling project에 서 Address.cs file을 추가하고 field를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686292055029&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Address
{
    public string? Building;
    public string Street;
    public string City;
    public string Region;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서 위와 같이 code를 작성하면 Street field에서 처럼 null가능한 부분에 대해서 다음과 같이 경고를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLrGoS/btsD8xkWHtA/d3AYK2aJedlWXVq5nR64B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLrGoS/btsD8xkWHtA/d3AYK2aJedlWXVq5nR64B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLrGoS/btsD8xkWHtA/d3AYK2aJedlWXVq5nR64B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLrGoS%2FbtsD8xkWHtA%2Fd3AYK2aJedlWXVq5nR64B0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;875&quot; height=&quot;160&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 non-nullable인 각 field에 대해 empty string을 아래와 같이 할당합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686292336668&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Address
{
    public string? Building;
    public string Street = string.Empty;
    public string City = string.Empty;
    public string Region = string.Empty;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 Address의 instance를 생성하고 각 속성에 아래와 같이 값을 할당합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686292476084&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Address address = new()
{
    Building = null,
    Street = null,
    City = &quot;Daegu&quot;,
    Region = &quot;KR&quot;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 Building이 아닌 Street의 null설정 부분에서 CS8625에 대한 경고를 보게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;489&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GRWef/btsEaZaoL9K/4fkhGdD2oNq0yqpmXsKed1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GRWef/btsEaZaoL9K/4fkhGdD2oNq0yqpmXsKed1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GRWef/btsEaZaoL9K/4fkhGdD2oNq0yqpmXsKed1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGRWef%2FbtsEaZaoL9K%2F4fkhGdD2oNq0yqpmXsKed1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;489&quot; height=&quot;171&quot; data-origin-width=&quot;489&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 예제에서 Street의 설정 부분에 있는 null다음에 아래와 같이 null 무효 연산자인 !를 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686292640188&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Street = null!,&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 경고 message는 사라지게 될 것입니다. 다음으로 Building과 Street에 대한 속성을 역참조하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686293239890&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(address.Building.Length);
Console.WriteLine(address.Street.Length);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이번에는 CS8602용 경고가 Street가 아닌 Building에 표시될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz9dKY/btsD5wUoMqg/e7gIXnLtFTkiCnMb520310/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz9dKY/btsD5wUoMqg/e7gIXnLtFTkiCnMb520310/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz9dKY/btsD5wUoMqg/e7gIXnLtFTkiCnMb520310/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz9dKY%2FbtsD5wUoMqg%2Fe7gIXnLtFTkiCnMb520310%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;111&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runtime에서는 여전히 Street에 대한 예외가 발생할 수 있지만 compiler는 Building에 대한 잠재적인 예외를 계속 경고할 것입니다. 이때 이를 피하기 위해서는 Length에 곧장 접근하는 대신 반환될 수 있는 null에 대해 null 조건 연산자를 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686297992930&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(address.Building?.Length);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 Building의 Length에 접근하는 문에서 null값(공백으로 표시)이 출력되지만 Street의 Length의 접근 시에는 runtime 예외가 발생할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;65&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mOb1b/btsD4HaMP6Z/p2XPXl0Q0bJ5hbfmCdv85K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mOb1b/btsD4HaMP6Z/p2XPXl0Q0bJ5hbfmCdv85K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mOb1b/btsD4HaMP6Z/p2XPXl0Q0bJ5hbfmCdv85K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmOb1b%2FbtsD4HaMP6Z%2Fp2XPXl0Q0bJ5hbfmCdv85K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;65&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;65&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 우리는 NRT가 단지 compiler에게 문제를 일으킬 수 있는 잠재적인 null값에 대하여 경고를 제공하도록 요청하는 것일 뿐, code에 대한 실질적인 동작을 바꾸는 것은 아니며 compile time에서 code의 정적인 분석을 수행할 뿐이라는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제까지의 설명은 null가능한 참조 type에 대한 것이며, 일반적인 참조 type은 non-nullable이 될 수 있고 값 type에서 사용했던 것과 같은 문법을 통해 nullable참조 type을 만들 수 있음을 말씀드리고자 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;참조 type에서의 ? 접미사는 type을 바꾸지 않습니다. type을 Nullable&amp;lt;T&amp;gt;로 바꾸는 값 type에서의 ? 접미사와는 다른 것입니다. 참조 type은 처음부터 null값을 가지며 nullable 참조 type에서 수행하는 모든 것은 compiler에게 null이 될 수 있음을 말하는 것으로 이것만으로 compiler는 개발자에게 경고를 해줄 필요가 없습니다. 하지만 code에서 null확인을 수행해야 하는 필요성을 없애는 것이 아닙니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 NRT를 구현하는 것과 더불어 code의 동작을 바꾸는 null값으로 작업하기 위한 언어기능에 대해 계속 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) null 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Null가능한 참조 type인지 null가능한 값 type변수가 현재 null값을 가지고 있는지를 확인하는 것은 그렇게 하지 않으면 NullReferenceException 예외가 error로 발생할 수 있기 때문에 중요합니다. null 변수는 아래와 같이 사용하기 전 null값인지를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686361373100&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (nullableVariable != null) //null 확인
{
	int length = nullableVariable.Length; // 위 조건이 없고 null이라면 이 지점에서 예외발생
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 7부터는 '!='를 대체할 수 있도록 ! 연산자를 아래와 같이 결합할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686361531756&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!(nullableVariable is null))
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 9부터는 위에서 보다 더 명확하게 null이 아님을 명시할 수 있는 not이 도입되었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686361662995&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (nullableVariable is not null)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 값이 null일 수 있는 변수를 사용하려는 경우에 있다면 null 조건부 연산자인 ? 를 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686361772235&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string authorName = null;
int x = authorName.Length;

int? y = authorName?.Length;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 변수를 결과에 할당하거나 변수가 null이면 3처럼 대체값을 사용하려 한다면 ?? null 병합 연산자를 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;null가능한 참조 type을 사용한다고 하더라도 여전히 null에 대한 non-nullable 매개변수를 확인하고 ArgumentNullException을 표시할 수 있어야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Method의 매개변수에서 null인지를 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수와 함께 method를 정의할 때 null값을 확인하는 건 좋은 습관입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#의 이전 version에서는 if문을 통해 매개변수의 null값을 확인하고 null이면 ArgumentNullException예외를 발생시킬 수 있었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686373838114&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void personMethod(Person manager, Person employee)
{
	if (manager == null)
	{
		throw new ArgumentNullException(nameof(manager));
	}
	if (employee == null)
	{
		throw new ArgumentNullException(nameof(employee));
	}
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 10에서는 매개변수가 null인 경우 예외를 발생시킬 수 있는 편리한 method가 도입되어 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686373929212&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void personMethod(Person manager, Person employee)
{
	ArgumentNullException.ThrowIfNull(manager);
	ArgumentNullException.ThrowIfNull(employee);
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 11 preview에서는 새로운 !!연산자를 도입하여 위와 동일한 동작을 수행시킬 수 있었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686374000491&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void personMethod(Person manager!!, Person employee!!)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 if와 예외를 발생시키는 문은 하위의 다른 문을 실행시키기 이전에 먼저 주입되고 실행되므로 특히 !!연산자는 C# 개발자 community에서 논란이 되기도 했는데 일부는 이러한 연산자를 사용하는 대신 매개변수에 attribute를 사용해 적용하는 걸 선호하기도 했고 .NET team은 이 기능을 통해 .NET library의 전체 code에서 10,000줄 이상을 줄일 수 있었다고 언급하기도 했습니다. 이것만으로 이 기능을 사용하기 충분히 좋다고 생각될 수 있지만 유감스럽게도 team은 이 기능을 최종적으로 삭제하기로 하였습니다. 따라서 !!연산자를 제외하고 if문이나 method를 null확인을 위해 사용해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;nullable은 위험성에 대한 경고만 줄 뿐 무엇인가를 강제하는 것이 아님을 기억해야 합니다. null과 관련된 compiler의 경고에 관해서는 아래글을 참고하시기 바랍니다.&lt;br /&gt;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/ compiler-messages/nullable-warnings.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;8. Class로부터의 상속&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 생성했던 Person type은 System.Object의 별칭인 object로부터 상속된 것입니다. 이제 Person type으로 상속하는 하위(sub) class를 생성해 봄으로서 상속에 관한 자세한 사항을 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyLibrary에서 Employee.cs이름의 새로운 file을 추가하고 아래와 같이 Person으로부터 파생되는 Employee class를 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686375583971&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace MyLibrary
{
	public class Employee : Person
	{
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyApp project의 Program.cs에서는 아래와 같이 Employee class의 instance를 생성하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686375777562&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Employee jun = new()
{
	Name = &quot;seong jun&quot;,
	DateOfBirth = new(year: 1985, month: 12, day: 24)
};
jun.WriteToConsole();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deb2tp/btsD4V0ZTkj/QcMWFErI31Ucz57z8cwkcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deb2tp/btsD4V0ZTkj/QcMWFErI31Ucz57z8cwkcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deb2tp/btsD4V0ZTkj/QcMWFErI31Ucz57z8cwkcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdeb2tp%2FbtsD4V0ZTkj%2FQcMWFErI31Ucz57z8cwkcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Employee class는 Person의 모든 member가 상속되었음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 기능 추가로 class 확장하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만들어진 Employee class는 확장을 위해 Employee고유의 member를 추가할 수 있습니다. Employee.cs에서 아래와 같이 직원번호과 고용된 날짜를 위한 2개의 속성을 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686387608049&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public string? EmployeeCode { get; set; }
public DateTime HireDate { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 jun의 직원번호(EmployeeCode)와 고용날짜(HireDate)를 설정하고 설정된 내용을 출력하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686387737393&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;jun.EmployeeCode = &quot;JJ001&quot;;
jun.HireDate = new(year: 2014, month: 11, day: 23);

Console.WriteLine($&quot;{jun.Name} was hired on {jun.HireDate:yyyy-MM-dd}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p8PRd/btsEb8YMdpi/tTVnXitUsnyAcFSZfaXkB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p8PRd/btsEb8YMdpi/tTVnXitUsnyAcFSZfaXkB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p8PRd/btsEb8YMdpi/tTVnXitUsnyAcFSZfaXkB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp8PRd%2FbtsEb8YMdpi%2FtTVnXitUsnyAcFSZfaXkB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Member 숨김&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 WriteToConsole method는 Person으로부터 상속된 것이며 Employee의 Name과 DateOfBirth를 표시하였습니다. 그런데 이 method를 Employee에 맞게 바꾸려 한다면 아마도 아래와 같이 할 수 있을 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686529620974&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public DateTime HireDate { get; set; }

public void WriteToConsole()
{
    Console.WriteLine(format:&quot;{0} was born on {1:yyyy-MM-dd} and hired on {2:yyyy-MM-dd}&quot;, arg0: Name, arg1: DateOfBirth, arg2: HireDate);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 예제를 실행하면 다음과 같은 응답을 생성하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOGZJT/btsEaNuhOE7/uuKc5xKkJktN9bs52bsJ3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOGZJT/btsEaNuhOE7/uuKc5xKkJktN9bs52bsJ3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOGZJT/btsEaNuhOE7/uuKc5xKkJktN9bs52bsJ3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOGZJT%2FbtsEaNuhOE7%2FuuKc5xKkJktN9bs52bsJ3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;85&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Visual Studio에서는 방금 전 추가한 method에서 Person으로부터의 method가 숨겨진다는 경고를 다음과 같이 표시할 것입니다. app을 build 하고 실행하게 되면 'Error List'창에도 해당 오류를 자세히 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwi4hI/btsEbGnRIUW/nzkSAk3WN1H0IEwzG5uotK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwi4hI/btsEbGnRIUW/nzkSAk3WN1H0IEwzG5uotK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwi4hI/btsEbGnRIUW/nzkSAk3WN1H0IEwzG5uotK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwi4hI%2FbtsEbGnRIUW%2FnzkSAk3WN1H0IEwzG5uotK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;875&quot; height=&quot;107&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경고 message에서 설명된 것처럼 method에서 인의적으로 Person의 method를 현재의 method로 바꿀 것임을 나타내는 new keyword를 사용하면 이러한 경고 message를 지울 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686530302219&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public new void WriteToConsole()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) this와 base&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# keyword 중에서는 현재 개체의 instance 혹은 상속의 대상이 되는 base class를 참조하기 위한 this와 base라는 2개의 keyword가 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this는 현재 개체의 instance를 나타내는 것으로서 예를 들어 Person class instance안에 member (비 정적 member)를 위해 this.DateOfBirth과 같은 방법을 사용하여 현재 개체의 instance에 속한 DateOfBirth filed에 접근할 수 있습니다. 물론 일반적인 경우 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DateOfBirth만을 사용하는 것만으로 아무런 문제없이 filed에 접근할 수 있지만 만약&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DateOfBirth라는 새로운 지역변수가 존재하는 경우라면 지역변수가 아닌 개체의 member로서 DateOfBirth를 참조한다고 명시해줄 필요가 있기 때문에 이때는 this를 붙여줘야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;base는 현재 개체가 상속하는 기반 class를 나타내는 것입니다. 예를 들어 Person class안에서 base.ToString()문을 사용한다면 이때는 해당 method를 구현하고 있는 base class의 member를 호출하는 것입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Member 재정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하다면 method를 숨기기보다는 override를 사용하는 편이 더 일반적입니다. 단 override는 기반 class에서 overriding을 허용하는 method에 virtual keyword를 적용했을 때만 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 jun의 변숫값을 console에 문자열로 표시하도록 하는 문을 아래와 같이 작성하고&lt;/p&gt;
&lt;pre id=&quot;code_1686531020732&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(jun.ToString());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 응답을 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkfRbE/btsD3qHn4oW/vkDMa7Lwjoh8E3kkLRLEB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkfRbE/btsD3qHn4oW/vkDMa7Lwjoh8E3kkLRLEB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkfRbE/btsD3qHn4oW/vkDMa7Lwjoh8E3kkLRLEB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkfRbE%2FbtsD3qHn4oW%2FvkDMa7Lwjoh8E3kkLRLEB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;74&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 ToString method는 System.Object로 부터 상속된 것이므로 type의 namespace와 이름을 표시할 것입니다. 이 상태에서 Person.cs에 person의 name과 type의 name을 표시하도록 아래와 같이 ToString method를 추가합니다. 이렇게 되면 ToString method의 동작을 바꿀 수 있게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686531313930&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public override string ToString()
{
    return $&quot;{Name} is a {base.ToString()}&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 base keyword는 파생 class에서 상속하거나 파생되는 해당 기반 class의 member에 접근할 수 있도록 하는 keyword입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 결과를 확인해 보시기 바랍니다. ToString method를 호출할 때 기반 class에서 구현된 ToString의 반환내용과 더불어 person의 name을 함께 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpz6p2/btsD4RRLOZg/3suUE4aYANNqTGoZ3PC7jK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpz6p2/btsD4RRLOZg/3suUE4aYANNqTGoZ3PC7jK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpz6p2/btsD4RRLOZg/3suUE4aYANNqTGoZ3PC7jK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpz6p2%2FbtsD4RRLOZg%2F3suUE4aYANNqTGoZ3PC7jK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;69&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;69&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Microsoft의 Entity Framework Core나 Castle의 DynamicProxy, CMS의 content model 최적화 같은 많은 실제 API들은 class에서 정의한 속성이 재정의 될 수 있도록 virtual로 표시되어야 합니다. 이때 어떤 method와 속성 member를 virtual로 할지는 신중하게 결정해야 합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 추상(abstract) class로 부터의 상속&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 6. Interface와 Class상속&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type이 가져야 할 기본 수준의 기능에 대한 일련의 member를 정의할 수 있는 interface에 대해 다뤄봤습니다. 이것만으로도 꽤 유용하긴 하지만 한 가지 큰 제한사항은 C# 8까지는 자체적으로 구현하는 어떤 것도 제공할 수 없다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 .NET Framework및 .NET Standard 2.1을 지원하지 않는 다른 platform에서 작동하는 class library를 생성할 때 특히 문제가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 상황에서는 순수 interface와 완전 구현 class의 중간 격인&amp;nbsp; 추상 class를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class가 abstract로 수식되면 해당 class는 완전하지 않음을 나타내기 때문에 instance를 생성할 수 없게 됩니다. 즉, instance가 생성되기 전 더 많은 구현이 필요한 것입니다. 예를 들어 System.IO.Stream class는 추상 class로서 모든 stream에서 필요할 수 있는 공통적인 기능을 구현하고 있지만 완전하지는 않아서 new Stream()으로 instance를 생성할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 insterface와&lt;/p&gt;
&lt;pre id=&quot;code_1686538595818&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface INoImplementation // C# 1.0
{
    void Alpha(); // 파생된 type에서 구현되어야 함
}
public interface ISomeImplementation // C# 8부터
{
    void Alpha(); // 파생된 type에서 구현되어야 함
    void Beta()
    {
        // 기본 구현으로서 재정의될 수 있음
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상 class가 존재할 때&lt;/p&gt;
&lt;pre id=&quot;code_1686538784760&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public abstract class PartiallyImplemented // C# 1.0
{
    public abstract void Gamma(); // 파생된 type에서 구현되어야 함
    public virtual void Delta() // 재정의될 수 있음
    {
        // 구현
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 interface과 추상 class에 대한 파생 class를 생성하면 아래와 같이 만들어질 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686538883633&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class FullyImplemented : PartiallyImplemented, ISomeImplementation
{
    public void Alpha()
    {
        // 구현
    }
    public override void Gamma()
    {
        // 구현
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 정말 구현된 class에서만 instance를 생성할 수 있고 다른 type에 대해서는 compile오류가 발생할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686538988351&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FullyImplemented a = new();

//에러
PartiallyImplemented b = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 상속 및 재정의 차단하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class에 sealed keyword를 사용하면 해당 class로부터 다른 개발자가 상속받는 것을 막을 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686551547074&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public sealed class MyType
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. NET에서 sealed가 사용된 사례로는 string class를 들 수 있습니다. Microsft는 string class에 몇몇 극한의 최적화를 구현하였고 때문에 이를 상속해 사용할 경우 부정적인 영향을 끼칠 수 있어서 string class로부터는 아예 상속을 구현할 수 없도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class에 있는 method도 마찬가지로 sealed keyword를 통해 누군가가 해당 method를 재정의하는 걸 막을 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686551833120&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MyType
{
	public virtual void MyMethod()
	{

	}
}

public class MyType2 : MyType
{
	public sealed override void MyMethod()
	{

	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 위 예제의 경우 아무도 MyType2에 있는 MyMethod를 재정의할 수 없습니다. sealed는 override method에서만 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 다형성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속된 method의 동작을 바꾸는 데는 2가지 방법이 있습니다. 하나는 new keyword(non-polymorphic inheritance)를 사용해 기반 class의 method를 숨기는 것과 override(polymorphic inheritance)를 사용해 기반 class의 method를 재정의하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 가지 방법 모두에서는 base keyword를 사용해 기반(super) class의 member에 접근할 수 있는데 그렇다면 이 둘 간의 차이점은 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 것은 모두 개체의 참조를 가진 변수의 유형에 따라 달라집니다. 예를 들어 Person type의 변수는 Person class 혹은 Person으로부터 파생된 모든 type을 가질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Employee.cs에서 ToString method를 재정의하여 employee에 대한 name과 code를 console로 출력하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686614554433&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public override string ToString()
{
    return $&quot;{Name}'s code is {EmployeeCode}&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 syAhn이라는 이름의 새로운 employee를 생성하고 Person type변수에 저장합니다. 그리고 이 둘의 변수에서 WriteToConsole과 ToString method를 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686614811225&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Employee ahnInEmployee = new()
{
    Name = &quot;syAhn&quot;,
    EmployeeCode = &quot;12345&quot;
};

Person ahnInPerson = ahnInEmployee;
ahnInEmployee.WriteToConsole();
ahnInPerson.WriteToConsole();

Console.WriteLine(ahnInEmployee.ToString());
Console.WriteLine(ahnInPerson.ToString());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음의 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ytAxQ/btsEcrcW1Fw/JlVbkkBmpLo7lKpESflt0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ytAxQ/btsEcrcW1Fw/JlVbkkBmpLo7lKpESflt0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ytAxQ/btsEcrcW1Fw/JlVbkkBmpLo7lKpESflt0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FytAxQ%2FbtsEcrcW1Fw%2FJlVbkkBmpLo7lKpESflt0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;110&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method가 new로 숨겨진 경우에 compiler는 해당 개체가 Employee의 method를 말하는 것인지를 알지 못하므로 Person에 대한 WriteToConsole method를 호출하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method가 virtual과 override를 통해 재정의된 경우에는 compiler는 비록 변수가 Person class로 정의되었어도 개체가 Employee class이므로 Employeee에서 구현된 ToString method를 호출합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Member의 한정자와 이에 대한 영향은 아래 표와 같이 요약할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Variable type&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Member modifier&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Method executed&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;In class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Person&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;WriteToConsole&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Person&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Employee&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;new&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;WriteToConsole&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Employee&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Person&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;virtual&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;ToString&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Employee&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Employee&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;override&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;ToString&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Employee&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적인 의견으로 new보다는 virtual과 override가 new보다는 많이 사용되는 듯합니다. 솔직히 다형성을 완전히 이해하지 않아도 virtual과 override정도의 개념만 알고 있으면 그것도 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;9. 계층적 상속에서 Casting 하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Type 간 casting은 type 간 converting과는 미묘하게 다릅니다. Casting은 16-bit integer와 32-bit integer 사이 혹은 sub class와 super class사이와 같이 비슷한 type 간에 이루어지는 것을 말하며 Converting은 문자열-숫자사이와 같이 비슷하지 않은 type간에 이루어지는 것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 암시적인 casting&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 예제에서는 파생된 type의 instance를 해당 base type의 변수(또는 base의 base type, 기타 등등)에 저장해 보았습니다. 실제 이러한 동작이 이루어질 때 이것을 암시적 casting이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 명시적 casting&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type명에 괄호를 사용하여 원하는 type으로 cast 하는 방법을 명시적 cast라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 aliceInPerson변수를 새로운 Employee로 할당하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686639703296&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Employee explicitAhn = ahnInPerson;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 Visual Studio에서는 다음과 같이 compile error를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;135&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuFetw/btsEa5BrfJK/Yk9iT0apNAfiuaqfXY4pL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuFetw/btsEa5BrfJK/Yk9iT0apNAfiuaqfXY4pL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuFetw/btsEa5BrfJK/Yk9iT0apNAfiuaqfXY4pL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuFetw%2FbtsEa5BrfJK%2FYk9iT0apNAfiuaqfXY4pL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;981&quot; height=&quot;135&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;135&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 할당된 변수 앞에 문을 변경하여 Employee type으로 cast 하도록 지정해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686640058684&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Employee explicitAhn = (Employee)ahnInPerson;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Casting 예외 피하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 처리로 compiler는 더 이상 error를 표시하지 않지만 ahnInPerson은 Employee가 아닌 Student처럼 파생된 type이 다를 수 있으므로 주의해야 합니다. 더 복잡한 code를 가진 실제 application에서 해당 변수의 현재 값은 Student의 instance로 설정될 수도 있으며 그러면 위의 예제는 InvalidCastException error를 유발할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Type을 확인하기 위한 is 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try 문을 사용해 위의 문제를 해결할 수 있지만 이 보다 더 나은 방법은 is keyword를 사용하여 다음과 같이 if문과 함께 개체의 type을 확인하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686640431991&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (ahnInEmployee is Employee)
{
    Console.WriteLine($&quot;{nameof(ahnInEmployee)} IS an Employee&quot;);
    Employee explicitAlice = (Employee)ahnInEmployee;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caKxz2/btsD4R5jBJO/gE9Law2WpBBz7Z7sKtjf8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caKxz2/btsD4R5jBJO/gE9Law2WpBBz7Z7sKtjf8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caKxz2/btsD4R5jBJO/gE9Law2WpBBz7Z7sKtjf8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaKxz2%2FbtsD4R5jBJO%2FgE9Law2WpBBz7Z7sKtjf8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;73&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상태에서 선언적 pattern을 사용하면 code를 더욱 간소화시키고 명시적인 cast의 수행을 생략할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686640681639&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (ahnInEmployee is Employee explicitAhn)
{
    Console.WriteLine($&quot;{nameof(ahnInEmployee)} IS an Employee&quot;);
    explicitAhn.WriteToConsole();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 ahnInEmployee가 Employee가 아닌 경우에 block의 문을 실행하고자 한다면 이전에는 ! (not) 연산자를 사용하여 아래와 같이 표현했지만&lt;/p&gt;
&lt;pre id=&quot;code_1686640811246&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!(ahnInEmployee is Employee))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 9 부터는 not keyword를 사용하여 아래와 같이 표현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686640849118&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (ahnInEmployee is not Employee)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Type을 cast 하기 위한 as 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;is의 대안으로 cast를 위해 as keyword를 사용할 수도 있습니다. as keyword는 type의 cast가 불가능한 경우 예외를 일으키는 대신 null을 반환한다는 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 as keyword를 통해 ahn을 cast 하고 반환값이 null인지 아닌지를 확인하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686648204114&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Employee? aliceAsEmployee = ahnInEmployee as Employee;
if (aliceAsEmployee is not null)
{
    Console.WriteLine($&quot;{nameof(aliceAsEmployee)} AS an Employee&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Null 변수 member로의 접근은 NullReferenceException error를 일으킬 수 있으므로 이것을 사용하기 전 항상 null여부를 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I8nDe/btsD3MpQ590/k9ROP8hggYsw2t24wRlXdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I8nDe/btsD3MpQ590/k9ROP8hggYsw2t24wRlXdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I8nDe/btsD3MpQ590/k9ROP8hggYsw2t24wRlXdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI8nDe%2FbtsD3MpQ590%2Fk9ROP8hggYsw2t24wRlXdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;72&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;되도록 이면 is와 as keyword를 사용하여 파생된 type사이에 casting을 시도할 때 발생할 수 있는 예외를 피하는 것이 좋습니다. is와 as를 사용하지 않으면 try~catch문을 통해서라도 InvalidCastException예외를 처리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;10. .NET Type의 상속과 확장&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET에는 사전에 내장된 수천 가지의 type을 포함하고 있습니다. 따라서 필요한 type을 직접 만들기보다는 필요한 동작에 대한 일부 혹은 전체를 상속받고 해당 기능을 재정의하거나 확장하기 위해 Microsoft의 type 중 하나로부터 파생하는 type을 생성함으로써 좀 더 유리한 출발을 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 예외 상속&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속에 대한 하나의 예로 exception으로부터 새로운 type을 파생해 볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyLibrary project에서 PersonException.cs이름의 class file을 추가하고 세 개의 생성자를 가진 PersonException이름의 class를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686701229835&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PersonException : Exception
{
    public PersonException() : base() { }
    public PersonException(string message) : base(message) { }
    public PersonException(string message, Exception innerException) : base(message, innerException) { }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 method와는 달리, 생성자는 상속되지 않습니다. 그러므로 예제에서는 명시적으로 생성자를 선언하고 명시적으로 System.Exception(또는 이것으로부터 파생시킨 exception class)에 구현된 base 생성자를 호출하여 예제의 PersonException에서 이들 생성자를 사용하고자 하는 개발자가 사용할 수 있도록 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서는 DateTime매개변수가 person의 DateOfBirth보다 더 이전의 시간인 경우 예외를 발생시키도록 하는 method를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686701757443&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void TimeTravel(DateTime when)
{
	if (when &amp;lt;= DateOfBirth)
	{
		throw new PersonException(&quot;DateTime Exception Error&quot;);
	}
	else
	{
		Console.WriteLine($&quot;Welcome to {when:yyyy}!&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 jun의 TimTravel method를 호출할 때 아래와 같이 날짜를 지정하여 호출하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686702021805&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try
{
    jun.TimeTravel(when: new(1999, 12, 31));
    jun.TimeTravel(when: new(1970, 06, 01));
}
catch (PersonException ex)
{
    Console.WriteLine(ex.Message);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uE3SL/btsEbGOZNBt/wC3luopZQiWMK2cheSJqp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uE3SL/btsEbGOZNBt/wC3luopZQiWMK2cheSJqp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uE3SL/btsEbGOZNBt/wC3luopZQiWMK2cheSJqp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuE3SL%2FbtsEbGOZNBt%2FwC3luopZQiWMK2cheSJqp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;75&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체적으로 예외 type을 정의할 때는 System.Exception에 내장된 것을 명시적으로 호출하는 동일한 세 개의 생성자를 제공해야 합니다. 아마도 상속받고자 하는 다른 exception은 더 많을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 상속이 불가능할 때 type 확장하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 sealed 한정자를 사용하면 상속을 막을 수 있다는 것을 언급한 바 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 sealed keyword를 System.String class에 적용하여 누구도 이를 상속받아 string에 대한 동작을 잠재적으로 바꿀 수 없도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 불구하고 여전히 string으로 새로운 method를 추가하고자 한다면 C# 3.0에서 도입된 extension method를 사용할 수 있습니다. 하지만 잠깐. 확장 method를 제대로 이해하기 위해서는 우선 static method부터 다시 살펴볼 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 기능 재사용을 위한 static method 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#의 첫 번째 버전부터 문자열이 email주소를 포함하는지에 대한 유효성검증과 같은 기능의 재사용을 위해 정적(static) method를 사용할 수 있었습니다. 이때 이러한 기능의 구현은 추후에 알아볼 정규표현식을 사용할 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyLibrary project에서 StringExtensions.cs 이름의 새로운 class file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686703922127&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class StringExtensions
{
    public static bool IsValidEmail(string input)
    {
        return Regex.IsMatch(input, @&quot;[a-zA-Z0-9\.-_]+@[a-zA-Z0-9\.-_]+&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 IsValidEmail method는 static이며 @문자 전후로 사용된 문자들이 유효한지를 확인하는 간단한 email pattern과 일치하는지를 확인하기 위해 Regex type을 사용하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 2가지 email 주소에 대한 유효성을 검증하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686704181542&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string email1 = &quot;aaaa@test.com&quot;;
string email2 = &quot;bbbb&amp;amp;test.com&quot;;

Console.WriteLine(&quot;{0} is a valid e-mail address: {1}&quot;, arg0: email1, arg1: StringExtensions.IsValidEmail(email1));
Console.WriteLine(&quot;{0} is a valid e-mail address: {1}&quot;, arg0: email2, arg1: StringExtensions.IsValidEmail(email2));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;79&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG8qs9/btsEaXDGjoP/VAnu6FXo0dK9ifH4ls9iH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG8qs9/btsEaXDGjoP/VAnu6FXo0dK9ifH4ls9iH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG8qs9/btsEaXDGjoP/VAnu6FXo0dK9ifH4ls9iH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG8qs9%2FbtsEaXDGjoP%2FVAnu6FXo0dK9ifH4ls9iH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;79&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;79&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제가 잘 작동하기는 하지만 확장 method를 사용하면 위의 예보다 작성해야 할 code를 훨씬 더 많이 줄일 수 있으며 해당 기능의 용법을 단순화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 기능 재사용을 위한 extension method 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;extension method안에서 static method를 생성하는 것만으로 extension method를 쉽게 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StringExtensions.cs에서 class앞에 static 한정자를 붙이고 type에도 this한정자를 아래와 같이 붙여줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686788791915&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static class StringExtensions
{
    public static bool IsValidEmail(this string input)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 compiler는 string type의 확장기능으로서 method가 처리됨을 알게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 email 주소가 유효한지 확인이 필요한 string 값에 extention method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686788983275&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;{0} is a valid e-mail address: {1}&quot;, arg0: email1, arg1: email1.IsValidEmail());
Console.WriteLine(&quot;{0} is a valid e-mail address: {1}&quot;, arg0: email2, arg1: email2.IsValidEmail());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 IsValidEmail method를 호출하기 위한 문법이 약간 단순해졌음을 알 수 있습니다. 그러나 이전에 문법을 그대로 사용하고자 한다면 그것도 여전히 유요 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IsValidEmail 확장 method는 IsNormalized처럼 string type의 실제 모든 instance method처럼 표시될 것입니다. 다만 Visual Studio에서는 해당 method가 확장 method인지를 나타내기 위한 아래화살표가 method의 icon으로 사용된다는 것만 차이가 있을 뿐입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4nrLZ/btsD5wfNEnZ/rr4YwUkRIU4c9ewP9DR9WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4nrLZ/btsD5wfNEnZ/rr4YwUkRIU4c9ewP9DR9WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4nrLZ/btsD5wfNEnZ/rr4YwUkRIU4c9ewP9DR9WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4nrLZ%2FbtsD5wfNEnZ%2Frr4YwUkRIU4c9ewP9DR9WK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;151&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;151&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 동일한 결과를 표시할 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;확장 method는 기존에 존재하던 instance method를 교체하거나 재정의될 수 없습니다. 예컨대 기존 Insert method를 그대로 확장 method로서 정의할 수 없습니다. 확장 method는 기존 동일한 이름과 특징을 가진 method가 존재하는 경우 IntelliSense에 표시되지는 않지만 해당 instance method를 호출하게 되면 확장 method를 대신 호출하게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지만 보면 확장 method가 그다지 유용해 보이지 않을 수 있지만 LINQ를 통해 data를 다루는 과정에서는 꽤 유용하게 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;11. Code 개선하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 C#에 대한 기본적인 것을 익히고 나면 이제 더 나은 code를 작성하기 위해 어떻게 해야 하는지를 고민할 필요가 있는데, 이를 위한 몇 가지 방법이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 경고를 오류로 취급하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 나은 code를 작성하기 위한 간단하면서도 효과적인 방법은 스스로가 compiler의 경고를 무시하지 않고 최대한 이를 수정하는 것입니다. 기본적으로 경고는 무시해도 application을 build 하는 데는 문제가 없지만 필요하면 compiler에게 이들에 대한 위험성 있는 경고를 무지하지 못하도록 요청할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy06 solution에서 WarningsAsErrors이름의 Console App project를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 사용자에게 이름을 입력하도록 요청하고 입력된 이름으로 인사말을 표시하도록 아래와 같이 기존의 문을 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686808448384&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.Write(&quot;Enter a name: &quot;);

string name = Console.ReadLine();

Console.WriteLine($&quot;Hello, {name} has {name.Length} characters!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 build 하면 성공적으로 build가 이루어지긴 하지만 2개의 compiler경고 또한 같이 생성되는 걸 알 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;...\Program.cs(3,15,3,33): warning CS8600: Converting null literal or possible null value to non-nullable type. &lt;br /&gt;...\Program.cs(5,40,5,44): warning CS8602: Dereference of a possibly null reference.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 build를 다시 수행하면 이번에도 정상적으로 build가 이루어지지만 경고는 사라지는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio에서는 Error List를 통해 기존의 경고 message를 계속 확인할 수 있습니다. 또한 경고 message가 사라지는 것은 build를 하는 경우로 rebuild를 하면 계속해서 경고 message를 표시합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio의 Build menu나 dotnet 명령줄 도구에서 clean을 사용하여 project를 'clean'할 수 있습니다. 그러면 경고 message는 다음 build시도 때 다시 나타나게 될 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 csproj(project file)에서 compiler에게 경고를 error로서 취급하도록 아래와 같이 요소를 추가하여 요청하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686809075680&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&amp;lt;TreatWarningsAsErrors&amp;gt;true&amp;lt;/TreatWarningsAsErrors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 project를 다시 build 하면 이번에는 build에 실패하고 2개의 error가 표시됨을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;47&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/093Sz/btsD8xrICS1/pTTTcKCveGvX0T27GoznKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/093Sz/btsD8xrICS1/pTTTcKCveGvX0T27GoznKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/093Sz/btsD8xrICS1/pTTTcKCveGvX0T27GoznKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F093Sz%2FbtsD8xrICS1%2FpTTTcKCveGvX0T27GoznKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;47&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;47&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 build를 다시 시도하면 경고 message도 사라졌지만 이번에 다시 build를 실행하면 여전히 build는 실패하게 됩니다. 따라서 해당 error를 수정하지 않으면 예제 project는 build 할 수 없을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 error를 수정하기 위해 ? nullable 연산자를 string 변수를 선언하는 부분에 추가하고 null값을 확인하고 변수가 null이면 app을 빠져나갈 수 있도록 아래와 같이 code를 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686809357367&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string? name = Console.ReadLine();
if (name == null)
{
    Console.WriteLine(&quot;You did not enter a name.&quot;);
    return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 예제를 build 하면 error 없이 build가 이루어질 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위의 예제와 같은 상황에서 ReadLine method는 실제 null을 반환하지 않습니다. 사용자가 어떠한 값도 입력하지 않으면 null이 아닌 공백을 반환합니다. 이 처럼 절대 null일 수 없는 상황이라면 null-forgiving 연산자를 접미사로 붙여 ReadLine을 호출하면 error를 간단하게 수정할 수 있습니다.&lt;br /&gt;
&lt;pre id=&quot;code_1686809861535&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string name = Console.ReadLine()!;​&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;되도록이면 경고를 무시하지 말고 경고를 error로 취급하여 이를 수정하는 것이 좋습니다. ReadLine method가 실제 null을 반환하지 않는다는 것을 compiler가 모르는 issue에 대해서는 개별적으로 해당 경고를 끌 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Warning wave&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새롭게 표시될 수 있는 경고와 error는 C# compiler가 release 될 때마다 추가될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 code에서 이렇게 추가된 경고 message가 표시된다면 이들 경고는 warning wave라고 하는 opt-in system에서 도입된 것입니다. Opt-in system은 명시적으로 이들 경고 message를 보겠다는 action을 취하지 않으면 기존의 code에서 새로운 경고 message는 볼 수 없다는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Warning wave는 csproj(project file)에서 AnalysisLevel요소를 사용함으로써 활성화할 수 있습니다. 예를 들어 .NET 7에서 도입된 warning wave warning을 disable하고자 한다면 AnalysisLevel을 6.0으로 아래와 같이 설정하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686811159940&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&amp;lt;AnalysisLevel&amp;gt;6.0&amp;lt;/AnalysisLevel&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에 AnalysisLevel에서 설정가능한 값으로는 다음과 같은 것들이 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.3256%;&quot;&gt;Level&lt;/td&gt;
&lt;td style=&quot;width: 82.6744%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.3256%;&quot;&gt;5.0&lt;/td&gt;
&lt;td style=&quot;width: 82.6744%;&quot;&gt;Warning wave를 5 warning까지로만 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.3256%;&quot;&gt;6.0&lt;/td&gt;
&lt;td style=&quot;width: 82.6744%;&quot;&gt;Warning wave를 6 warning까지로만 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.3256%;&quot;&gt;7.0&lt;/td&gt;
&lt;td style=&quot;width: 82.6744%;&quot;&gt;Warning wave를 7 warning까지로만 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.3256%;&quot;&gt;latest (default)&lt;/td&gt;
&lt;td style=&quot;width: 82.6744%;&quot;&gt;모든 version의 warning wave를 사용하도록 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.3256%;&quot;&gt;preview&lt;/td&gt;
&lt;td style=&quot;width: 82.6744%;&quot;&gt;Preview wave를 포함한 모든 version의 warning wave를 사용하도록 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.3256%;&quot;&gt;none&lt;/td&gt;
&lt;td style=&quot;width: 82.6744%;&quot;&gt;warning을 아예 사용하지 않습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compiler에게 warning을 error로서 취급하도록 지정했다면 사용되는 warning wave warning은 error를 생성하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Warning wave 5 진단도구는 C# 9에서 추가된 것으로 다음과 같은 몇몇 예를 포함하고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.3953%;&quot;&gt;CS8073&lt;/td&gt;
&lt;td style=&quot;width: 88.6047%;&quot;&gt;표현식의 결과는 항상 false또는 true여야 한다는 것을 의미합니다. ==와 != 연산자는 struct type s의 instance를 null과 비교할때 항상 false나 true만을 반환합니다.&lt;br /&gt;if (s == null) { } // CS8073: The result of the expression is always 'false'&lt;br /&gt;if (s != null) { } // CS8073: The result of the expression is always 'true'.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.3953%;&quot;&gt;CS8892&lt;/td&gt;
&lt;td style=&quot;width: 88.6047%;&quot;&gt;동기식 주 진입점인 method가 존재하므로 해당 method가 주요 진입점으로 사용되지 않을것임을 의미합니다. 일반적인 Main method와 async인것이 동시에 존재한다면 일반적인것이 우선적으로 처리되므로 compiler는 async인것은 절대 사용되지 않는다라는 것을 경고하게 되는 것입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Warning wave 6 진단도구는 C# 10에서 추가되었습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.7675%;&quot;&gt;CS8826&lt;/td&gt;
&lt;td style=&quot;width: 85.2325%;&quot;&gt;Partial method 선언에 다른 특징이 있음을 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Warning wave 7 진단도구는 C# 11에서 추가되었습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.65114%;&quot;&gt;CS8981&lt;/td&gt;
&lt;td style=&quot;width: 90.3489%;&quot;&gt;Type의 이름은 ascii 소문자만을 포함해야 함을 말합니다. C# keyword는 모두 ascii 소문자입니다. 이 경고는 모든 type이 향후 C# keyword와 충돌하지 않도록 합니다. 도구, 예를 들어 gRPC service를 위해 .NET proxy를 생성하는 Google의 design 도구와 같은 곳에서 생성된 일부 source code는 해당 경고를 발동하게 됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 link를 통해서는 warning wave에 어떤 warning이 추가되었는지를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/warning-waves&quot;&gt;C# Compiler warning waves | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1686812912693&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;C# Compiler warning waves&quot; data-og-description=&quot;C# warning waves are optional warnings that can be reported on code where previously a warning wouldn't have been reported. They represent practices that could be harmful, or potentially elements that may be breaking changes in the future.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/warning-waves&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/warning-waves&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bvaWDi/hyS0bUTfYK/VHc6QwFaahrHDqDTtoec11/img.png?width=64&amp;amp;height=64&amp;amp;face=0_0_64_64&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/warning-waves&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/warning-waves&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bvaWDi/hyS0bUTfYK/VHc6QwFaahrHDqDTtoec11/img.png?width=64&amp;amp;height=64&amp;amp;face=0_0_64_64');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;C# Compiler warning waves&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;C# warning waves are optional warnings that can be reported on code where previously a warning wouldn't have been reported. They represent practices that could be harmful, or potentially elements that may be breaking changes in the future.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Code를 개선하기 위한 분석도구 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET analyzer는 잠재적인 문제점을 찾아 이들에 대한 수정방안을 제시하는데 그중에서 StyleCop은 더 나은 C# code를 작성하기 위해 가장 일반적으로 사용되는 분석도구 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy06 solution에서 CodeAnalyzing Conole App project를 추가하는데 이때 'Do not use top-level statements'부분을 check 하고 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S63pz/btsEEyLnXRH/4OCiQ2C6fKxZOznW6m5e2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S63pz/btsEEyLnXRH/4OCiQ2C6fKxZOznW6m5e2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S63pz/btsEEyLnXRH/4OCiQ2C6fKxZOznW6m5e2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS63pz%2FbtsEEyLnXRH%2F4OCiQ2C6fKxZOznW6m5e2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;108&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project가 생성되면 csproj(project file)에서 아래의 요소를 추가하여 StyleCop.Analyzers에 대한 참조를 추가하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686878142016&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
	&amp;lt;PackageReference Include=&quot;StyleCop.Analyzers&quot; Version=&quot;1.2.0-*&quot;&amp;gt;
		&amp;lt;PrivateAssets&amp;gt;all&amp;lt;/PrivateAssets&amp;gt;
		&amp;lt;IncludeAssets&amp;gt;
			runtime; build; native; contentfiles; analyzers;
			buildtransitive
		&amp;lt;/IncludeAssets&amp;gt;
	&amp;lt;/PackageReference&amp;gt;
&amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Version에서 '1.2.0-*'을 사용하면 현재 시점으로 가장 최신 version인 1.2.0-beta.435가 적용됩니다. 만약 GA로 release 된다면 그때는 version에서 1.2.0만 지정하여 *를 제거할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 뒤 project에 stylecop.json이름의 file을 추가하고 아래와 같이 StyleCop을 위한 설정을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686879678425&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;$schema&quot;: &quot;https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json&quot;,
    &quot;settings&quot;: {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 $schema부분은 code editor에서 stylecop.json을 수정할 때 IntelliSense를 가능하게 합니다. settings영역 안에 내부 설정 부분으로 이동 후 Ctrl + Space key를 누르면 IntelliSense는 설정가능한 정확한 하위요소를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;195&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cagf4H/btsEaYbvtmH/KKi2J7FTc7pz8R10U0uBa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cagf4H/btsEaYbvtmH/KKi2J7FTc7pz8R10U0uBa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cagf4H/btsEaYbvtmH/KKi2J7FTc7pz8R10U0uBa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcagf4H%2FbtsEaYbvtmH%2FKKi2J7FTc7pz8R10U0uBa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;466&quot; height=&quot;195&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;195&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 CodeAnalyzing project file로 돌아와 application의 배포 시에는 stylecop.json이 포함되지 않도록 하고 AdditionalFiles로 개발 중에서만 처리될 수 있도록 하는 설정요소를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686881277862&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
	&amp;lt;None Remove=&quot;stylecop.json&quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;
&amp;lt;ItemGroup&amp;gt;
	&amp;lt;AdditionalFiles Include=&quot;stylecop.json&quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 console대신 debug output window로 message를 출력하도록 하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686881355929&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Main(string[] args)
{
    Debug.WriteLine(&quot;Hello, World!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 project를 build 하면 잘못된 것으로 판단되는 것에 대한 경고 message가 표시될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uviFT/btskcn8VFKj/I4k2FKpntl1Sk09TGjukq0/img.png&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;143&quot; data-is-animation=&quot;false&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 message는 using 지시문은 namespace선언 내부에 들어가 있어야 함을 말해주고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;warning&amp;nbsp;SA1200:&amp;nbsp;Using&amp;nbsp;directive&amp;nbsp;should&amp;nbsp;appear&amp;nbsp;within&amp;nbsp;a&amp;nbsp;namespace&amp;nbsp;declaration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 경고 message 억제하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code를 추가하거나 구성설정을 포함하여 경고 message자체를 표시하지 않게 하기 위한 몇 가지 방법이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 assembly수준의 attribute를 아래와 같이 추가하면 관련된 경고 message를 표시하지 않게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686882844093&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[assembly:SuppressMessage(&quot;StyleCop.CSharp.OrderingRules&quot;, &quot;SA1200:UsingDirectivesMustBePlacedWithinNamespace&quot;, Justification = &quot;Reviewed.&quot;)]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#pragma 지시자문을 경고 message를 발생시키는 구문주위에 아래와 같이 적용해도 위와 동일한 효과를 가질 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686882960886&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma warning disable SA1200 // Using directives should be placed correctly
using System.Diagnostics;
#pragma warning restore SA1200 // Using directives should be placed correctly&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 stylecop.json file에서 namespace외부에서 using문을 사용할 수 있도록 하는 설정을 추가할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686883051656&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;settings&quot;: {
    &quot;orderingRules&quot;: {
        &quot;usingDirectivesPlacement&quot;: &quot;outsideNamespace&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project를 build 하면 warning SA1200 관련된 경고 message는 더 이상 표시되지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 stylecop.json에서 usingDirectivesPlacement의 설정값을 preserve로 바꾸면 using문은 namespace내/외부에서 모두 사용할 수 있도록 설정됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686883167365&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;orderingRules&quot;: {
    &quot;usingDirectivesPlacement&quot;: &quot;preserve&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Code의 수정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다른 경고에 대한 수정을 위해 csproj project file에서 문서화를 위해 자동적으로 XML file을 생성하는 요소를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686896805812&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&amp;lt;GenerateDocumentationFile&amp;gt;true&amp;lt;/GenerateDocumentationFile&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 stylecop.json에서는 company name이나 copyright 문자열등과 같이 문서화에 필요한 값을 제공하기 위한 구성 옵션을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686896908492&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;orderingRules&quot;: {
    &quot;usingDirectivesPlacement&quot;: &quot;preserve&quot;
},
&quot;documentationRules&quot;: {
    &quot;companyName&quot;: &quot;CLIEL&quot;,
    &quot;copyrightText&quot;: &quot;Copyright (c) CLIEL. All rights reserved.&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 company와 copyright등과 함께 file header에 사용될 comment를 추가하고 using System.Diagnostics;을 namespace안으로 옮겨 둡니다. 또한 class와 method에 명시적인 접근 한정자와 XML comment를 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686897329764&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace CodeAnalyzing;

using System.Diagnostics;

/// &amp;lt;summary&amp;gt;
/// Main Class.
/// &amp;lt;/summary&amp;gt;
public class Program
{
    /// &amp;lt;summary&amp;gt;
    /// Main Method.
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name=&quot;args&quot;&amp;gt;
    /// A string array of arguments.
    /// &amp;lt;/param&amp;gt;
    public static void Main(string[] args)
    {
        Debug.WriteLine(&quot;Hello, World!&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 build한뒤 bin/Debug/net7.0 folder를 보면 자동적으로 생성된 CodeAnalyzing.xml이름의 file을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686897458190&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;doc&amp;gt;
    &amp;lt;assembly&amp;gt;
        &amp;lt;name&amp;gt;CodeAnalyzing&amp;lt;/name&amp;gt;
    &amp;lt;/assembly&amp;gt;
    &amp;lt;members&amp;gt;
        &amp;lt;member name=&quot;T:CodeAnalyzing.Program&quot;&amp;gt;
            &amp;lt;summary&amp;gt;
            Main Class.
            &amp;lt;/summary&amp;gt;
        &amp;lt;/member&amp;gt;
        &amp;lt;member name=&quot;M:CodeAnalyzing.Program.Main(System.String[])&quot;&amp;gt;
            &amp;lt;summary&amp;gt;
            Main Method.
            &amp;lt;/summary&amp;gt;
            &amp;lt;param name=&quot;args&quot;&amp;gt;
            A string array of arguments.
            &amp;lt;/param&amp;gt;
        &amp;lt;/member&amp;gt;
    &amp;lt;/members&amp;gt;
&amp;lt;/doc&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CodeAnalyzing.xml file은 문서화 file로 변환하기 위한 DocFX와 같은 도구를 사용해 처리될 수 있습니다. 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.jamescroft.co.uk/building-net-project-docs-with-docfx-on-github-pages/&quot;&gt;Building .NET project docs with DocFX on GitHub Pages - James Croft&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● StyleCop의 일반적인 권장사항들&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code file에서는 아래와 같은 여러 항목을 순서대로 정렬해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;External alias directive&lt;/li&gt;
&lt;li&gt;Using directive&lt;/li&gt;
&lt;li&gt;Namespace&lt;/li&gt;
&lt;li&gt;Delegate&lt;/li&gt;
&lt;li&gt;Enum&lt;/li&gt;
&lt;li&gt;Interface&lt;/li&gt;
&lt;li&gt;Struct&lt;/li&gt;
&lt;li&gt;Classe&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class, record, struct 또는 interface안에서는 다음 항목들을 순서대로 정렬해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Field&lt;/li&gt;
&lt;li&gt;Constructor&lt;/li&gt;
&lt;li&gt;Destructor (finalizer)&lt;/li&gt;
&lt;li&gt;Delegate&lt;/li&gt;
&lt;li&gt;Event&lt;/li&gt;
&lt;li&gt;Enum&lt;/li&gt;
&lt;li&gt;Interface&lt;/li&gt;
&lt;li&gt;Property&lt;/li&gt;
&lt;li&gt;Indexer&lt;/li&gt;
&lt;li&gt;Method&lt;/li&gt;
&lt;li&gt;Struct&lt;/li&gt;
&lt;li&gt;Nested class와 record&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StyleCop에 관한 모든 규칙에 관해서는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/DOCUMENTATION.md&quot;&gt;StyleCopAnalyzers/DOCUMENTATION.md at master &amp;middot; DotNetAnalyzers/StyleCopAnalyzers &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>event</category>
      <category>generic</category>
      <category>Interface</category>
      <category>NULL</category>
      <category>struct</category>
      <category>값 type</category>
      <category>상속</category>
      <category>참조 type</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/596</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D#entry596comment</comments>
      <pubDate>Tue, 20 Feb 2024 09:11:32 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 5. OOP (Object-Oriented Programming)</title>
      <link>https://cliel.tistory.com/entry/C-12-%EA%B3%BC-NET-8-5-OOP-Object-Oriented-Programming</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서는 OOP를 통해 자신만의 type을 만들 수 있습니다. 이를 위해 data를 저장하기 위한 field와 동작을 수행하는 method를 포함해 type이 가질 수 있는 member들에 대해 encapsul화와 같은 OOP개념을 사용해 볼 것입니다. 여기에 더해 tuple syntax support, out variables, inferred tuple names 그리고 default literals과 같은 언어기능과 간단한 동작을 수행하기 위한 연산자와 지역함수를 정의하는 방법도 함께 살펴보고자 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. OOP&amp;nbsp;(Object-Oriented&amp;nbsp;Programming)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;현실 세계의 개체는 자동차나 사람과 같은 것이지만 programming에서의 개체는 제품이나 은행 계좌와 같이 현실 세계의 무언가를 나타내는 경우가 많으며 더 추상적인 것일 수도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;C#에서 우리는 개체의 type을 정의하기 위해 class와 record 또는 struct와 같은 C# keyword를 사용합니다. class와 struct의 차이에 관한 것은 추후에 알아볼 테지만 type을 개체의 청사진 또는 template으로 생각할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;OOP의 개념을 요약하면 대략 아래와 같이 설명할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Capsule화(Encapsulation) : 개체와 관련된 data와 동작의 결합을 말합니다. 예를 들어 '은행계좌'라는 개체는 '잔액'과 '예금주'라는 data를 가질 수 있고 '입금'과 '출금'이라는 동작을 가질 수 있습니다. 캡슐화할 때는 개체의 내부 상태에 대한 접근이나 변경을 외부로부터 제한하는 방식을 통해 동작이나 data로의 접근을 통제할 수 있습니다.&lt;/li&gt;
&lt;li&gt;합성(composition) : 개체를 무엇으로 구성할지에 관한 것입니다. 예를 들어 자동차라는 개체는 4개의 wheel개체와 몇몇 좌석개체 그리고 engine과 같은 다른 부분으로 구성될 수 있습니다.&lt;/li&gt;
&lt;li&gt;집합(Aggregation) : 개체와 결합될 수 있는 것을 말합니다. 예를 들어 사람은 자동차의 구성요소가 아니지만 운전석에 자리 잡게 되면 자동차의 driver가 됩니다. 즉, 2개의 분리된 개체가 서로 결합하여 새로운 component로 만들어지게 되는 것입니다.&lt;/li&gt;
&lt;li&gt;상속(Inheritance) : 기반 혹은 super class로 부터 파생되어 하위 class를 생성함으로써 code를 재사용하는 것을 말합니다. 기반 class의 모든 기능이 상속되어 파생 class에서 사용할 수 있게 되는데 예를 들어 기반 혹은 super Exception class는 모든 예외에 대한 동일한 구현을 가진 몇 가지 member를 가지고 있으며 sub 혹은 파생 SqlException class에서는 이들 member를 상속하여 database연결 속성과 같이 SQL Database 예외가 발생할 때와 관련된 확장된 member를 가지게 됩니다.&lt;/li&gt;
&lt;li&gt;다형성(Polymorphism) : 파생된 class에서 상속된 동작을 override 하도록 허용하는 것으로서 이를 통해 파생된 class에서는 본래 class와 다른 동작을 제공할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2 Class Library 만들기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class library assembly는 type을 쉽게 배포가능한 단위(DLL file)로 group화 합니다. 단위 test를 배울 때를 제외하곤 지금까지 code를 작성하기 위해 console application만을 생성해 왔습니다. 여러 project에 걸쳐 재사용가능한 code를 만들려면 Microsoft가 하는 것처럼 class library assembly로 만들어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Class library 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재사용 가능한 .NET class library를 만들기 위해 우선 csStudy05라는 solution을 생성하고 그 안에 'NetLibraryStandard2'이름의 Class Library project를 추가합니다. NetLibraryStandard2.csproj file을 열어보면 아래와 같이 .NET 8을 target으로 하는 .NET SDK 8에 의해 project가 만들어졌으며 따라서 해당 project는 다른 .NET 8 호환 assembly에만 참조될 수 있음을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684649843588&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;

  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net8.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;

&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상태에서 아래와 같이 TargetFramework를 'netstandard2.0'로 변경하고 C# 12 compiler를 사용한다는 것을 명시적으로 지정하기 위한 요소와 모든 C# file로 정적 System.Console class를 정적 import하기 위한 요소를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684650089970&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
    &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;TargetFramework&amp;gt;netstandard2.0&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;LangVersion&amp;gt;12&amp;lt;/LangVersion&amp;gt;
        &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
        &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
    &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;ItemGroup&amp;gt;
	    &amp;lt;Using Include=&quot;System.Console&quot; Static=&quot;true&quot; /&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C# 12 compiler를 사용한다고 지정하더라도 .NET platform을 동일하게 맞춰주지 않으면 일부 언어기능을 사용할 수 없는 경우가 있습니다. 예를 들어 C# 8에서 도입된 interface의 기본구현은 .NET Standard 2.1을 필요로 하며 C# 11에서 도입된 required keyword는 .NET 7의 attribute를 필요로 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 csproj file을 저장한뒤 기본적으로 생성한 Class1.cs file로 삭제합니다. 그리고 project를 build 하여 이후에 다른 project에서도 참조가능하도록 설정합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;최신의 C#언어와 .NET platform 기능을 사용하기 위해서는 .NET 8 Class Library를 생성해야 합니다. 하지만 .NET Core, .NET Framework, Xamarin과 같은 이전 .NET platform을 지원하려면 .NET Standard 2.0 class library를 생성해야 합니다. 기본적으로 .NET Standard 2.0은 C# 7.0 compiler를 사용하지만 이 설정은 변경할 수 있기 때문에 .NET Standard 2.0 API로 제한한다고 하더라도 최신 SDK와 compiler에 대한 혜택을 여전히 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Namespace에 class를 정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 필요한 class를 정의할 차례입니다. 'NetLibraryStandard2' project에 Person.cs file을 추가하고 기존 구문을 모두 삭제한 뒤 아래와 같이 CLIEL.Shared라는 Namespace를 설정합니다. 이는 해당 file에서 정의된 type을 명시하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684650896409&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace CLIEL.Shared;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;가능한한 논리적으로 명명된 namespace로 class를 생성하는 것이 좋습니다. namespace이름은 예를 들어 숫자와 관련된 경우 System.Numerics와 같이 domain고유의 이름이 될 수 있습니다. 이 경우 예제에서는 Person, BackAccount 그리고 WonderOfTheWorld와 같은 type을 생성할 텐데 이들에 대한 전형적인 이름을 정하지는 않았으므로 CLIEL.Shared로 사용할 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 public keyword를 사용한 Person class를 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684651562682&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace CLIEL.Shared;

public class Person
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제의 public C# keyword는 class이전에 와야 합니다. 이 keyword는 접근 한정자로서 해당 class library외부의 다른 모든 code로부터 접근을 허용하기 위한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 명시적으로 public keyword를 적용하지 않는다면 해당 class를 정의한 assembly안에서만 접근이 가능하게 됩니다.&amp;nbsp; 아무것도 지정하지 않은 class는 암시적인 접근한정자로 internal을 사용하기 때문입니다. 예제에서 해당 class는 외부 assembly에서도 접근가능하도록 할 것이기 때문에 public으로 class를 정확히 지정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target이 .NET 6.0 이후로 맞춰져 있고 따라서 C# 10 이후를 사용한다면 code를 간소화하기 위해 semicolon으로 namespace를 선언하고 중괄호를 제거하는 것으로 끝낼 수 있습니다. 따라서 type정의는 들여 쓰기를 하지 않아도 됩니다. 이는 file-scoped namespace선언이라는 것으로 file당 하나의 file-scoped namespace를 가질 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서는 언어기능을 명시적으로 C#11을 사용할 것이라 csproj file에서 명시하였으므로 가능한 형태이며 만약 그렇게 하지 않은 경우라면 예제는 이전 version의 style로 다음과 같이 만들어야 합니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1684652529906&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace CLIEL.Shared
{
    public class Person
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 접근 한정자의 이해&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상가 예제의 class에는 public keyword를 사용하였으며 해당 keyword가 접근한정자에 해당합니다. public은 외부 class library를 포함해 외부에서 해당 class로 접근이 가능하도록 합니다. 만약 class에 별도의 한정자를 적용하지 않는다면 암시적으로 internal이 적용되며 이는 해당 class가 정의된 같은 assembly안에서만 접근이 가능하도록 합니다. 예제는 외부에서 접근이 가능하도록 만들 것이므로 명시적으로 public을 적용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class내부의 다른 class처럼 class가 중첩된 경우 내부 class는 private 한정자를 적용하여 상위 class에서 접근하지 못하게 할 수 있습니다. 또한 .NET 7에서는 file한정자가 도입되었는데 이는 code file안에서만 접근이 가능하도록 하는 것으로서 하나의 file에 여러 class를 정의하는 경우 유용하게 사용될 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;file 접근 한정자에 관해서는 아래 글을 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/&quot;&gt;C# reference - C# | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1705979065149&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;C# reference - C#&quot; data-og-description=&quot;C# reference&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/co5cqY/hyU85rrHPI/jVNY9zfYTlXRNdkfc7grUK/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/co5cqY/hyU85rrHPI/jVNY9zfYTlXRNdkfc7grUK/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;C# reference - C#&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;C# reference&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔하게 사용되는 한정자로는 public과 internal이 있습니다. internal은 한정자를 지정하지 않은 경우 기본적으로 적용되기는 하지만 되도록이면 internal이라도 code의 가독성을 위해 명시적으로 internal을 지정해 주는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Member의 이해&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 type은 아직까지는 capsule화된 어떠한 member도 가지고 있지 않습니다. 이제 몇 가지 member를 추가할 텐데 member는 field, method가 될 수 있고 이 둘 모두에 대한 특수한 version이 될 수도 있습니다. member가 될 수 있는 각각의 항목은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Field : data를 저장하는데 사용되며 또한 3가지로 분류될 수 있습니다. 그중 첫 번째는 Constant라는 것으로 compiler가 해당 field의 값에 대해 값을 읽는 모든 code에 그대로 복사하여 처리함으로써 field의 값이 바뀔 수 없도록 합니다. 두 번째는 Read-Only인데 이는 class가 instance화 된 후에 값이 바뀔 수 없도록 하는 것입니다. 다만 instance화 되는 시점에 data는 외부로부터 설정될 수 있습니다. 마지막 세 번째는 Event로 하나 또는 그 이상의 method를 참조하는 data를 말합니다. 해당 method는 어떤 사건이 발생할 때 실행되는 것으로서 button의 click을 예로 들 수 있습니다. Event에 관한 자세한 내용은 추후에 알아볼 것입니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Method : 특정 구문을 실행하는데 사용되는 것으로 '함수'부분을 배워볼 때 몇 가지 예제를 만들어본 적 있었습니다. 또한 method는 4가지 정도로 분류될 수 있는데 그중 첫 번째는 Contructor(생성자)라고 하는 것으로 new keyword를 통해 class의 instance를 memory에 할당할 때 실행되는 method입니다. 두 번째 Property는 data를 저장하거나 읽을 때 실행되는 것으로 data는 일반적으로 field에 저장하지만 외부에 저장하거나 계산되어 처리될 수도 있습니다. 일반적으로 속성은 field의 memory 주소를 노출하기보다는 field를 capsule화 하는데 선호되는 방법입니다. 세 번째 Indexer는 'array'구문인 []을 사용해 data를 저장하거나 값을 가져올 때 실행되는 것이며 마지막 네 번째는 type을 대상으로 +나 /와 같은 연산자를 사용할 때 실행되는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Class의 Instance생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 class를 사용하려면 해당 class에 대한 instance를 생성해야 하는데 만약 class가 정의된 것과 다른 project에서 사용하는 경우라면 해당 project에서 사용하고자 하는 class를 가진 assembly를 참조해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 이러한 과정을 거쳐보기 위해 csStudy05 solution에서 PeopleApp이라는 이름의 Console App project를 추가합니다. project가 생성되고 나면 해당 project에서 mouse 오른쪽 button을 눌러 'Add Project Reference...' menu를 선택합니다. 이어서 'NetLibraryStandard2' project를 확인하고 checkbox에 check 한 후 OK button을 눌러 해당 project를 참조추가한 뒤 project를 build 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Type을 사용하기 위한 namespace importing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Person class를 사용하기 위한 구문을 작성할 것입니다. PeopleApp의 Program.cs에서 기존 구문을 모두 삭제하고 Person class에 대한 namespace를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684718691358&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using CLIEL.Shared;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;물론 위의 namespace를 전역적으로 import 할 수 있으나 project에서 namespace가 import 되는 Program.cs file이 있으며 사용할 type의 import문이 해당 file의 위에 존재한다면 해당 code를 읽는 누구에게나 명확해질 수 있는 장점도 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Peron type의 instance를 생성하고 해당 instance를 통해 자체적인 설명을 출력하도록 합니다. 아래 예제의 new keyword는 개체를 memory에 할당하며 모든 내부 data를 초기화합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684719322133&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person kim = new();
Console.WriteLine(kim.ToString());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 보면 ToString이라는 method를 호출하고 있는데 실제 Person class를 보면 아무것도 존재하지 않습니다. 그럼 어떻게 ToString method가 사용될 수 있을까? 이런 것이 가능한 이유는 잠시 후 알아보기로 하고 일단 project를 실행하여 아래와 같은 결과가 표시되는지를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eiJ8yh/btsDJg5l2wd/aJuqFHtDruKfyMQHMVtpJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eiJ8yh/btsDJg5l2wd/aJuqFHtDruKfyMQHMVtpJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eiJ8yh/btsDJg5l2wd/aJuqFHtDruKfyMQHMVtpJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeiJ8yh%2FbtsDJg5l2wd%2FaJuqFHtDruKfyMQHMVtpJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;71&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● using alias를 통한 namespace충돌 방지하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔한 경우는 아니지만 같은 이름의 type을 가진 2개의 namespace가 존재할 수도 있는데 이 둘의 namespace가 import 되면 ambiguity를 유발할 수 있습니다. 예를 들어&lt;/p&gt;
&lt;pre id=&quot;code_1684720339718&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Suncheon.Gagokdong.cs
namespace Suncheon
{
	public class Gagokdong
	{
	}
}
// Miryang.Gagokdong.cs
namespace Miryang
{
	public class Gagokdong
	{
	}
}
// Program.cs
using Suncheon;
using Miryang;

Gagokdong g = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제의 경우 compiler는 아래와 같이 Error CS0104를 발생시키게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;'Gagokdong' is an ambiguous reference between 'Suncheon.Gagokdong' and 'Miryang.Gagokdong'&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우에는 namespace 중 하나에 alias를 정의함으로써 namespace가 구별될 수 있도록 해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684720592261&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Suncheon;
using m = Miryang;

Gagokdong g1 = new();
m.Gagokdong g2 = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● using alias를 통한 type명 변경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alias가 필요한 또 다른 상황은 type의 이름을 변경하는 경우입니다. 예를 들어 Environment class를 많이 사용하는 경우라면 해당 type을 이름을 간소하게 변경해 아래와 같이 사용할 수 있을 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684720702566&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Env = System.Environment;

WriteLine(Env.OSVersion);
WriteLine(Env.MachineName);
WriteLine(Env.CurrentDirectory);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 개체의 이해&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 예제의 Person class는 명시적으로 어떤 type으로부터 상속되도록 구현하지 않았지만 모든 type은 직간접적으로 System.Object라는 이름의 특별한 type으로 부터 상속됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ToString method의 구현은 System.Object안에 이루어진 것이며 전체 namespace와 type명을 출력하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person class로 다시 돌아가 아래와 같이 compiler에게 Person은 System.Object type으로 부터 상속됨을 명시적으로 알려줄 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684720970143&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person : System.Object
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A class를 B class가 상속받을 때 A는 기반(base) 또는 superclass라고 하며 B는 파생(derived) 또는 subclass라고 합니다. 따라서 이 경우 System.Object는 기반(superclass) class가 되며 Person은 파생(subclass)이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 C#에서 object keyword는 System.Object의 별칭에 해당하므로 아래와 같은 방법으로도 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684721167872&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person : object
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● System.Object로 부터의 상속&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 처럼 Person class를 명시적으로 object로부터 상속받게 한 뒤 object가 가진 모든 member를 확인해 보도록 하겠습니다. code에서 object keyword를 click 하고 F12 key를 누르거나 object keyword를 mouse 오른쪽 button을 눌러 'Go to Definition'을 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 Microsoft가 정의한 System.Object type과 해당 member들을 다음과 같이 확인해 볼 수 있습니다. 아직까지는 모든 항목에 대한 상속을 이해할 필요가 없지만 여기서 ToString이름의 method가 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684721595503&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	public class Object
	{
		//
		// Summary:
		//     Initializes a new instance of the System.Object class.
		public Object();

		//
		// Summary:
		//     Allows an object to try to free resources and perform other cleanup operations
		//     before it is reclaimed by garbage collection.
		~Object();

		//
		// Summary:
		//     Determines whether the specified object instances are considered equal.
		//
		// Parameters:
		//   objA:
		//     The first object to compare.
		//
		//   objB:
		//     The second object to compare.
		//
		// Returns:
		//     true if the objects are considered equal; otherwise, false. If both objA and
		//     objB are null, the method returns true.
		public static bool Equals(Object objA, Object objB);
		//
		// Summary:
		//     Determines whether the specified System.Object instances are the same instance.
		//
		// Parameters:
		//   objA:
		//     The first object to compare.
		//
		//   objB:
		//     The second object to compare.
		//
		// Returns:
		//     true if objA is the same instance as objB or if both are null; otherwise, false.
		public static bool ReferenceEquals(Object objA, Object objB);
		//
		// Summary:
		//     Determines whether the specified object is equal to the current object.
		//
		// Parameters:
		//   obj:
		//     The object to compare with the current object.
		//
		// Returns:
		//     true if the specified object is equal to the current object; otherwise, false.
		public virtual bool Equals(Object obj);
		//
		// Summary:
		//     Serves as the default hash function.
		//
		// Returns:
		//     A hash code for the current object.
		public virtual int GetHashCode();
		//
		// Summary:
		//     Gets the System.Type of the current instance.
		//
		// Returns:
		//     The exact runtime type of the current instance.
		public Type GetType();
		//
		// Summary:
		//     Returns a string that represents the current object.
		//
		// Returns:
		//     A string that represents the current object.
		public virtual string ToString();
		//
		// Summary:
		//     Creates a shallow copy of the current System.Object.
		//
		// Returns:
		//     A shallow copy of the current System.Object.
		protected Object MemberwiseClone();
	}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;class에 상속이 지정되지 않으면 System.Object로부터 기본적으로 상속이 이루어집니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Class의 기본 생성자 정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#12부터는 Class의 일부로서 하나의 생성자를 정의할 수 있습니다. 이를 기본생성자(primary constructor)라고 하는데 문법은 record에서의 positional data member와 동일하지만 실제 동작에서는 약간의 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본래 class를 정의할 때 생성자는 class내부에 위치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707462433842&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public class AnimalClass
{
  public AnimalClass(string Name, string Species)
  {

  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 기본생성자를 적용하게 되면 Class의 정의를 더욱 단순하게 구현할 수 있고&lt;/p&gt;
&lt;pre id=&quot;code_1707462433843&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public class AnimalClass(string Name, string Species)
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 class처럼 매개변수에 필요한 값을 전달하여 instance를 생성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707462433843&quot; class=&quot;ebnf&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;AnimalClass ac = new(&quot;Nyao&quot;, &quot;Cat&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 record와 다르게 class에서는 매개변수에 필요한 속성에 public을 자동으로 구현해 주지 않기 때문에 값이 들어간 속성은 class외부에서 접근할 수 없습니다. 따라서 필요한 속성은 아래와 같이 추가해 줘야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707462433844&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public class AnimalClass(string name, string species)
{
  public string Name { get; set; } = name;
  public string Species { get; set; } = species;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707462433844&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;AnimalClass ac = new(&quot;Nyao&quot;, &quot;Cat&quot;);
Console.WriteLine(ac.Name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;67&quot; data-origin-height=&quot;20&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl8qZI/btsEaXDEYKS/7PAOeJdWqES5ZSbiBZHow0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl8qZI/btsEaXDEYKS/7PAOeJdWqES5ZSbiBZHow0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl8qZI/btsEaXDEYKS/7PAOeJdWqES5ZSbiBZHow0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl8qZI%2FbtsEaXDEYKS%2F7PAOeJdWqES5ZSbiBZHow0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;67&quot; height=&quot;20&quot; data-origin-width=&quot;67&quot; data-origin-height=&quot;20&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수가 없는 생성자를 추가하는 경우에도 아래와 같은 방법으로 기본값을 기본 생성자로 전달할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707462433845&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public class AnimalClass(string name, string species)
{
  public string Name { get; set; } = name;
  public string Species { get; set; } = species;

  public AnimalClass() : this(&quot;Mong&quot;, &quot;Dog&quot;) { }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707462433846&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;AnimalClass ac = new();
Console.WriteLine(ac.Name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 URL을 참고하면 class와 구조체에 대한 더 많은 기본 생성자정보를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/primary-constructors&quot;&gt;Declare and use primary constructors in classes and structs - C# | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1707462433846&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fecf9/hyVcbZtLmC/7NPY7kcyOzsk3wMxlV5p01/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/primary-constructors&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/primary-constructors&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-description=&quot;Learn how and when to declare primary constructors in your class and struct types. Primary constructors provide concise syntax to declare constructor parameters available anywhere in your type.&quot; data-og-title=&quot;Declare and use primary constructors in classes and structs - C#&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/primary-constructors&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/primary-constructors&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fecf9/hyVcbZtLmC/7NPY7kcyOzsk3wMxlV5p01/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Declare and use primary constructors in classes and structs - C#&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Learn how and when to declare primary constructors in your class and struct types. Primary constructors provide concise syntax to declare constructor parameters available anywhere in your type.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. Field에 data 저장하기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 person 관한 정보를 저장하기 위해 class에 몇몇 field를 정의할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Field 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 자신의 이름과 생일이라는 날짜를 가지고 있으므로 이 두 개의 값으로 person을 구성할 것입니다. 이때 person class내부에서는 이 둘의 값을 capsule화 하여 외부에서 해당 값이 보일 수 있도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person class에서 사람의 이름과 생일을 저장하기 위한 2개의 public field를 선언하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684736505850&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person : object
{
	public string? Name;
	public DateTime DateOfBirth;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 DateOfBirth field에 대한 data type에는 여러 가지를 선택할 수 있습니다. .NET 6에서는 DateOnly type을 도입했는데 이 것은 시간을 제외하고 날짜만 저장하도록 합니다. 반면 DateTime은 날짜와 시간 모두를 저장할 수 있습니다. 또는 DateTimeOffset을 선택할 수도 있는데 이는 날짜, 시간, time zone까지도 저장합니다. 어느 것을 선택할지는 얼마만큼 상세한 정보의 저장이 필요한가에 달려 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#8부터 compiler는 string과 같은 참조 type이 null값을 가질 수 있는 경우 경고를 표시할 수 있으며 잠재적으로 NullReferenceException예외를 발생시킬 수 있습니다. .NET 6 이후부터 SDK는 이들 경고를 기본적으로 표시하도록 하고 있는데 해당 참조type에 ?의 접미사를 붙여 null을 허용하고 있음을 표시할 수 있고 그러면 경고문구는 나타나지 않을 것입니다. null가능과 이와 같은 경우를 어떻게 처리할 수 있는지에 대한 자세한 사항은 추후에 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;field는 하나의 field에 여러 값을 저장해야 하는 경우 array나 list와 dictionary와 같은 collection을 포함해 어떤 type이든 사용할 수 있습니다. 예제에서는 단지 하나의 이름과 하나의 생일값만을 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 접근 한정자(Access modifier)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 한정자는 capsule화의 일부로서 member를 어떠한 방식으로 노출할지를 지정하는 것입니다. 예제의 class에서는 명시적으로 field에 public keyword를 적용했는데 만약 이렇게 하지 않았더라면 암시적으로 private이 적용되어 해당 member는 오로지 class의 내부에서만 접근 가능한 형태가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근한정자는 총 4개의 종류가 있으며 추가로 2개의 접근 한정자 keyword를 결합해 field나 method와 같은 class의 member에 적용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;private&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;type안에서만 접근가능하며 아무런 접근한정자도 적용하지 않으면 private이 기본으로 적용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;internal&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;type내부와 같은 assembly안의 모든 type에서 접근할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;protected&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;type내부와 해당 type을 상속한 type에서만 접근할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;public&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;외부 어디서든 접근할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;internal protected&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;type내부와 같은 assembly안의 모든 type 그리고 해당 type을 상속한 모든 type에서 접근할 수 있습니다. 'internal or protected'형태의 접근한정자와 동일합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;private protected&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;type내부와 type을 상속한 그리고 같은 assembly의 모든 type에서 접근할 수 있습니다. 'internal and protected'형태의 접근한정자와 동일하며 해당 한정자는 C# 7.2이후에서만 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;member에 암시적으로 적용되는 접근 한정자를 사용한다고 하더라도 이를 생략하지 말고 모든 member에 접근한정자 중 하나를 명시적으로 지정하는 것이 좋습니다. 일반적으로 field는 private 혹은 protected를 사용하며 field의 값을 설정하고 가져오기 위해 public속성을 생성하여 접근을 제한하는 데 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Field에 값 설정하고 가져오기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 kim을 instance화 한 이후에는 이름과 생일을 설정할 수 있는 구문을 추가할 수 있고 설정된 값을 아래와 같이 불러올 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684803169783&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kim.Name = &quot;kimya&quot;;
kim.DateOfBirth = new DateTime(1980, 12, 01);

Console.WriteLine($&quot;{kim.Name}은 {kim.DateOfBirth}에 태어남.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 확인하는데 보간문자열을 사용하고 있으며 실제 예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0YtuQ/btsDHrmihAo/UhS5TdiTaCsnIekEkxB1d1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0YtuQ/btsDHrmihAo/UhS5TdiTaCsnIekEkxB1d1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0YtuQ/btsDHrmihAo/UhS5TdiTaCsnIekEkxB1d1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0YtuQ%2FbtsDHrmihAo%2FUhS5TdiTaCsnIekEkxB1d1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;75&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 위 결과는 해당 PC의 지역화와 문화권설정에 따라 다르게 보일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 DateOfBirth는 몇 가지 형식화된 값을 가져올 수 있는데 dddd로 요일의 이름을, d로는 날짜를 표현할 수 있습니다. MMMM은 월의 이름을 나타내며 소문자 m으로는 시간에서 분을 의미합니다. yyyy는 전체 연도를 표시하며 yy는 2 자릿수 연도를 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 C# 3.0부터 도입된 중괄호를 사용한 '개체 초기자' 구문을 사용해 field를 초기화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 구문아래에 'hong'이름의 새로운 person개체를 생성하기 위한 구문을 추가합니다. 해당 예제에서 값을 가져올 때는 DateOfBirth속성에 특정 형식화를 적용하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684803870295&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person hong = new() {
	Name = &quot;honggd&quot;,
	DateOfBirth = new (1982, 09, 29)
};

Console.WriteLine($&quot;{kim.Name}은 {kim.DateOfBirth: yyyy-MM-dd}에 태어남.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 결과는 다음과 같을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4HpIU/btsDHrmihAu/JDgOF1fl5LW1jp0AoZ8NvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4HpIU/btsDHrmihAu/JDgOF1fl5LW1jp0AoZ8NvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4HpIU/btsDHrmihAu/JDgOF1fl5LW1jp0AoZ8NvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4HpIU%2FbtsDHrmihAu%2FJDgOF1fl5LW1jp0AoZ8NvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;67&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Enum type을 사용한 값 저장&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 값이 option의 제한된 설정중 하나여야 하는 경우가 있습니다. 예를 들어 세계 7대 불가사의 중 좋아하는 한 가지를 선택하는 것처럼 다수 중에 하나의 값을 설정하거나 또는 이와 관련해 가보고 싶은 곳에 대한 bucket list처럼 여러 값을 설정하는 것입니다. 이러한 형태의 data를 저장하기 위해서 우리는 enum type을 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enum type은 내부적으로 문자열설명의 순람표(lookup table)와 함께 정수값을 사용함으로써 하나 또는 그 이상의 선택적 data를 저장하기에 효율적인 방법을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NetLibraryStandard2 project에 WondersOfTheAncientWorld.cs이름의 file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684823456058&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace NetLibraryStandard2
{
	public enum WondersOfTheAncientWorld
	{
		GreatPyramidOfGiza,
		HangingGardensOfBabylon,
		StatueOfZeusAtOlympia,
		TempleOfArtemisAtEphesus,
		MausoleumAtHalicarnassus,
		ColossusOfRhodes,
		LighthouseOfAlexandria
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 Person.cs에서는 아래와 같은 field를 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1684823569932&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public WondersOfTheAncientWorld FavoriteAncientWonder;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 해당 field로 값을 설정하고 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684823742105&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person hong = new()
{
	Name = &quot;honggd&quot;,
	DateOfBirth = new(1982, 09, 29)
};

hong.FavoriteAncientWonder = NetLibraryStandard2.WondersOfTheAncientWorld.StatueOfZeusAtOlympia;

Console.WriteLine($&quot;{hong.Name} : {hong.FavoriteAncientWonder} / {(int)hong.FavoriteAncientWonder}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 project를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P2bFY/btsDLLpJnwq/krbAVKob7MlpX5WFtEKk00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P2bFY/btsDLLpJnwq/krbAVKob7MlpX5WFtEKk00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P2bFY/btsDLLpJnwq/krbAVKob7MlpX5WFtEKk00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP2bFY%2FbtsDLLpJnwq%2FkrbAVKob7MlpX5WFtEKk00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;72&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enum값은 내부적으로 효율성을 위해 int값을 사용해 저장합니다. 이때 int값은 자동적으로 0부터 할당되므로 예제의 enum에서 세 번째 불가사의항목은 2 값을 가지게 됩니다. 물론 필요에 따라서는 이 순서를 따르지 않은 다른 값을 저장하는 것도 가능합니다. 다만 값과 일치되는 것을 찾을 수 없는 경우에는 이름대신 int값을 출력하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Enum type을 사용한 다수값 저장하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 예로든 bucket list의 경우에는 enum에 대한 array나 collection을 생성할 수도 있을 것입니다. 하지만 더 좋은 방법이 있는데 enum flag를 사용한 단일값으로 여러 선택값을 결합하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 enum에 아래와 같이 [Flags] attribute를 적용하고 명시적으로 각 항목에 byte값이 사용되도록 한 뒤 실제 bit column마다 다른 값을 표현하여 값을 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684824982656&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Flags]
public enum WondersOfTheAncientWorld : byte
{
	None = 0b_0000_0000, //0
	GreatPyramidOfGiza = 0b_0000_0001, //1
	HangingGardensOfBabylon = 0b_0000_0010, //2
	StatueOfZeusAtOlympia = 0b_0000_0100, //4
	TempleOfArtemisAtEphesus =  0b_0000_1000, //8
	MausoleumAtHalicarnassus = 0b_0001_0000, //16
	ColossusOfRhodes = 0b_0010_0000, //32
	LighthouseOfAlexandria = 0b_0100_0000 //64
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 명시적으로 memory에 저장된 bit를 확인할 때 겹치지 않는 각 항목에 값을 할당하였습니다. 또한 enum type에 는 System.Flags attribute를 적용했기 때문에 값이 반환될 때 int값 대신 자동적으로 comma로 분리된 다수의 값과 일치하는 문자열을 반환할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 enum type은 내부적으로 int 변수를 사용하지만 그리 큰 값은 필요하지 않기 때문에 byte를 사용하면 4byte가 아닌 1byte만 있으면 되므로 75% 정도의 필요한 memory를 절약할 수 있습니다. 또 다른 예로 요일을 위한 enum을 정의할 때도 단지 7개 정도의 항목이 있으면 되기 때문에 byte의 사용이 유리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 통해 만약 Hanging Gardens of Babylon과 Mausoleum at Halicarnassus를 포함하는 bucket list를 표현하고자 한다면 16과 2에만 1bit의 설정을 주면 됩니다. 다시 말해 18 값을 저장하는 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;64&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;32&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;16&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서는 아래와 같이 BucketList field를 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1684826011550&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public WondersOfTheAncientWorld FavoriteAncientWonder;
public WondersOfTheAncientWorld BucketList;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 |(OR bit 논리) 연산자를 사용해 enum값을 결합하여 bucket list를 설정하는 구문을 추가합니다. 혹은 enum type으로 형변환하는 숫자 18의 값을 사용해 저장할 수도 있습니다. 다만 code를 읽기 어렵게 만들 수 있기 때문에 숫자를 직접적으로 사용하는 것은 권하지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684826358037&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hong.BucketList = WondersOfTheAncientWorld.HangingGardensOfBabylon | WondersOfTheAncientWorld.MausoleumAtHalicarnassus;
//혹은 이렇게도 가능 hong.BucketList = (WondersOfTheAncientWorld)18;
Console.WriteLine($&quot;{hong.Name}의 bucket list {hong.BucketList}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cALLCU/btsDIvaSkwq/o9Pl0ea1ioQ5PZI8iHyFO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cALLCU/btsDIvaSkwq/o9Pl0ea1ioQ5PZI8iHyFO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cALLCU/btsDIvaSkwq/o9Pl0ea1ioQ5PZI8iHyFO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcALLCU%2FbtsDIvaSkwq%2Fo9Pl0ea1ioQ5PZI8iHyFO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;72&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분리된 option의 결합을 저장할 때는 enum값을 사용합니다. 이때 option이 최대 8개라면 byte를, 16개라면 ushort를, 32개라면 uint를, 64 이상이라면 ulong을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Collection을 사용한 다수의 값 저장&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 person에 대한 자녀를 저장해야 한다면 이를 위한 field도 추가해야 하며 이를 위해 어떠한 type이든 정렬된 collection을 저장할 수 있는 generic List&amp;lt;T&amp;gt; collection type을 사용할 것입니다. 이것은 자녀가 현재 person과 관련이 있지만 person자체에 대한 일부가 아닌 class의 instance이므로 class에 집계를 적용하는 하나의 예가 될 수 있습니다. 참고로 collection에 관해서는 추후에 자세히 다룰 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 person의 자녀를 나타내는 여러 Person intance를 저장할 field를 아래와 같이 선언합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684921139230&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public WondersOfTheAncientWorld BucketList;
public List&amp;lt;Person&amp;gt; Children = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;Person&amp;gt;은 Person에 대한 List입니다. 따라서 Children이름의 속성 type은 Person intance의 list에 해당하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 collection은 item이 추가되기 전에 새로운 intance로 초기화되어야 합니다. 그렇지 않으면 field는 null이 되고 Add와 같은 member의 사용을 시도하게 되면 runtime 예외를 발생시키게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) generic(일반화) collection&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;T&amp;gt; type에서 &amp;lt;&amp;gt;는 C#에서 2005년 C# 2.0에서 도입된 일반화라 불리는 기능으로서 collection을 강력한 type으로 만들 수 있습니다. 이로 인해 compiler는 collection에 저장될 수 있는 개체의 type을 특정할 수 있고 따라서 code의 성능과 정확성을 향상됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강력한 type은 정적인 type과는 다른 의미를 가집니다. 예전 System.Collection type은 정적 type으로서 System.Object를 다루기 위한 것이었습니다. 그러나 새로운 System.Collection.Generic type은 강력한 type의 &amp;lt;T&amp;gt; instance를 다루기 위한 정적 type이라고 할 수 있습니다. (irony 하게도 generic이라는 용어는 좀 더 구체적인 정적 type을 사용할 수 있다는 것을 의미합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 hong에 대한 2명의 자녀를 추가하고 얼마나 많은 자녀를 가지고 있는지, 그리고 그 자녀에 대한 이름이 무엇인지를 표시하는 구문을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684922314003&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hong.Children.Add(new Person { Name = &quot;kim&quot; }); // C# 3.0 부터
hong.Children.Add(new() { Name = &quot;lee&quot; }); // C# 9.0 부터

Console.WriteLine($&quot;{hong.Name}에게는 {hong.Children.Count} 명의 자녀가 있음 =&amp;gt; &quot;);

for (int i = 0; i &amp;lt; hong.Children.Count; i++)
{
	Console.WriteLine($&quot; {hong.Children[i].Name}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;참고로 예제는 for문을 사용했으나 collection을 열거하기 위한 것으로 foreach문의 사용도 가능합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bimiUn/btsDGKlMWw1/wcVf1a6zFOlVB9duXj5wdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bimiUn/btsDGKlMWw1/wcVf1a6zFOlVB9duXj5wdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bimiUn/btsDGKlMWw1/wcVf1a6zFOlVB9duXj5wdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbimiUn%2FbtsDGKlMWw1%2FwcVf1a6zFOlVB9duXj5wdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;99&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) 정적 field&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 지금까지 만들어온 field는 모두 intance member이며 생성된 class의 각 instance에 대한 각각의 field에 다른 값이 존재합니다. 예제에서도 kim과 hong Person은 서로 다른 Name값을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 때로는 모든 instance에 걸쳐 공유되는 단 하나의 값을 가지는 field가 필요할 때도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 field만이 정적 member가 될 수 있는 것은 아니기에 정적 member라고 하는데 static filed를 통해 이를 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NetLibraryStandard2 project에 BankAccount.cs이름의 새로운 class file을 생성하고 해당 class에 3개의 field를 추가합니다. 이중 2개는 intance field이며 다른 하나는 static field입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684925750693&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class BankAccount
{
	public string AccountName;
	public decimal Balance;
	public static decimal InterestRate;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BankAccount의 각 intance는 자체적인 AccountName과 Balance값을 가지지만 이들 모든 instnace는 하나의 InterestRate값을 공유할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 InterestRate에 값을 설정하고 2개의 BankAccount에 대한 intance를 생성하는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684926140680&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BankAccount.InterestRate = 0.012M;

BankAccount kbAccount = new();
kbAccount.AccountName = &quot;kb&quot;;
kbAccount.Balance = 2400;

Console.WriteLine(format: &quot;{0} earned {1:C} interest.&quot;, arg0: kbAccount.AccountName, arg1: kbAccount.Balance * BankAccount.InterestRate);

BankAccount hcAccount = new();
hcAccount.AccountName = &quot;hc&quot;;
hcAccount.Balance = 98;

Console.WriteLine(format: &quot;{0} earned {1:C} interest.&quot;, arg0: hcAccount.AccountName, arg1: hcAccount.Balance * BankAccount.InterestRate);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz19R8/btsDNqr58de/dUOhkZmOVeAvYDUEvKxLgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz19R8/btsDNqr58de/dUOhkZmOVeAvYDUEvKxLgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz19R8/btsDNqr58de/dUOhkZmOVeAvYDUEvKxLgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz19R8%2FbtsDNqr58de%2FdUOhkZmOVeAvYDUEvKxLgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;88&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 1:C는 형식화 code로서 .NET에게 number를 통화형식으로 사용할 것을 알려주게 됩니다. 사용되는 통화형식은 설치된 운영체제의 설정을 따릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 현재 thread의 속성을 설정함으로써 통화기호나 다른 data형식을 결정하는 문화권을 조정할 수 있습니다. 예를 들어 영국으로의 문화권을 설정하고자 한다면 Program.cs상단에 아래와 같이 code를 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684926482716&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(&quot;en-GB&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 언급했듯이 field만이 정적 member가 될 수 있는 것은 아닙니다. Constructor(생성자), method, property 등도 정적 member가 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) constant field&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;field의 값이 절대 바뀔 수 없는 경우라면 const keyword를 사용해 compile time에 실제값을 할당할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 Person에 대한 인종을 설정하는 string constant를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684973398977&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public List&amp;lt;Person&amp;gt; Children = new();
public const string Species = &quot;Homo Sapiens&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constant field의 값을 가져오려면 class의 intance이름이 아닌 class자체의 이름을 사용해야 합니다. Program.cs에서 아래와 같이 hong의 이름과 함께 인종을 표시하는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684973555880&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;{hong.Name} is a {Person.Species}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q8YMc/btsDHmycJWk/4bcZ49JbqXRI8sv9sNLulK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q8YMc/btsDHmycJWk/4bcZ49JbqXRI8sv9sNLulK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q8YMc/btsDHmycJWk/4bcZ49JbqXRI8sv9sNLulK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq8YMc%2FbtsDHmycJWk%2F4bcZ49JbqXRI8sv9sNLulK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;78&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft type에 대한 const field의 예로 System.Int32.MaxValue나 System.Math.PI 등을 들 수 있습니다. 이들의 값은 절대 바뀔 수 없습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;const사용 시에는 2가지 사항에 대해 주의해야 합니다. 우선 값은 반드시 선언과 동시에 할당되어야 하며 string이나 bool 또는 숫자같이 실제 표현가능한 값이어야 합니다. const field에 대한 모든 참조는 compile time에 실제값으로 바뀌게 되므로 향후 version에서 값이 바뀌게 될 때 새로운 값을 가져오기 위해 참조하는 모든 assembly를 recompile 하지 않으면 값은 반영되지 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) 읽기 전용 field&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 바뀔 수 없는 field에 대한 또 다른 방법으로 read-only field가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 readonly field를 선언하고 거주 중인 행성의 이름을 저장합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684974276431&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public readonly string HomePlanet = &quot;Earth&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 hong의 이름과 함께 위에서 저장한 행성의 이름을 표시하는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684974344191&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;{hong.Name} is a {hong.HomePlanet}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;77&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JgHFW/btsDQfcLkNC/db6CAYMX5FuhX5zxFO0Ee0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JgHFW/btsDQfcLkNC/db6CAYMX5FuhX5zxFO0Ee0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JgHFW/btsDQfcLkNC/db6CAYMX5FuhX5zxFO0Ee0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJgHFW%2FbtsDQfcLkNC%2Fdb6CAYMX5FuhX5zxFO0Ee0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;77&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;77&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;readonly field는 runtime에서 값을 읽어 들일 수 있으며 실행가능한 모든 구문에서 표현될 수 있습니다. 따라서 값의 설정을 위해 생성자를 사용하거나 field에 값을 직접 할당할 수 있습니다. readonly field에 대한 모든 참조는 live 참조로서 관련된 모든 기능은 호출 code에서 정확하게 반영될 것입니다. 이것이 const와 readonly와의 가장 중요한 차입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경우에 따라서는 static readonly field를 선언할 수 있습니다. 이렇게 되면 type에 대한 모든 intance에 걸쳐 값이 해당 값을 공유할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(11) 생성자를 통한 field의 초기화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로 field는 runtime에서 초기화가 필요한 경우도 있습니다. 이러한 동작은 class로 new keyword를 사용해 intance를 생성할 때 호출되는 생성자를 통해 구현할 수 있습니다. 생성자자는 모든 field가 type을 사용하는 code에 의해 설정되기 전에 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 읽기 전용 field인 HomePlanet다음에 새로운 읽기전용 field를 정의하고 생성자에서 Name과 Instantiated field를 설정하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685001967059&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public readonly string HomePlanet = &quot;Earth&quot;;
public readonly DateTime Instantiated;

public Person()
{
	Name = &quot;Unknown&quot;;
	Instantiated = DateTime.Now;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 Program.cs에서 새로운 person에 대한 intance를 생성하고 초기 field값을 표시하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685002097514&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person blankPerson = new();

Console.WriteLine(format: &quot;{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.&quot;, arg0: blankPerson.Name, arg1: blankPerson.HomePlanet, arg2: blankPerson.Instantiated);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 결과를 다음과 같을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7dK9U/btsDKy5ft60/ThOj9UmV0ktJ4pFkcSiIfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7dK9U/btsDKy5ft60/ThOj9UmV0ktJ4pFkcSiIfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7dK9U/btsDKy5ft60/ThOj9UmV0ktJ4pFkcSiIfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7dK9U%2FbtsDKy5ft60%2FThOj9UmV0ktJ4pFkcSiIfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;71&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(12) intance 생성 시 속성에 설정 강제하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;C# 11에서는 required 한정자를 도입하였는데 이를 속성이나 field에 사용한다면 compiler는 해당 속성이나 field의 값을 instance생성 시 설정하도록 요구하게 됩니다. 참고로 C# 11이므로 .NET 7 이상을 필요로 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;csStudy05 solution에서 MyLibrary이름의 class library project를 .NET 8에(.NET 7부터 지원함) 맞춰 생성합니다. 그리고 해당 project에 Book.cs이름의 file을 추가하고 여기에 4개의 속성을 아래와 같이 추가합니다. 이때 아래 속성 중 2개는 required를 적용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706145175650&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;namespace CLIEL.Shared
{
	public class Book
	{
		public required string? Isbn { get; set; }
		public required string? Title { get; set; }
		public string? Author { get; set; }
		public int PageCount { get; set; }
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;예제에서 3개의 string 속성은 nullable입니다. 속성이나 field에 required를 적용하는 것이 null이 될 수 없다는 것을 뜻하는 것이 아닙니다. 단지 속성이나 field에 할당처리가 되어야 한다는 것을 말하는 것이기 때문에 명시적으로 null을 설정할 수도 있습니다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Program.cs에서 Book에 대한 intance를 Isbn과 Title속성을 설정하지 않고 생성하려고 하면 다음과 같이 오류를 발생시키게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpXvVn/btsDNpGI47z/0kILqjtWwDiqdiKHCHkcR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpXvVn/btsDNpGI47z/0kILqjtWwDiqdiKHCHkcR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpXvVn/btsDNpGI47z/0kILqjtWwDiqdiKHCHkcR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpXvVn%2FbtsDNpGI47z%2F0kILqjtWwDiqdiKHCHkcR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;114&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 개체 초기화 구문을 통해 아래와 같이 2개의 required속성을 설정하도록 하면 정상적으로 instance가 생성될 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706145175652&quot; class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;Book book = new()
{
	Isbn = &quot;123-4567890000&quot;,
	Title = &quot;The Little Prince&quot;
};

Console.WriteLine(&quot;{0}: {1} written by {2} has {3:N0} pages.&quot;, book.Isbn, book.Title, book.Author, book.PageCount);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다시 MyLibrary project의 Book.cs로 돌아와 이번에는 생성자 한쌍을 정의하는 구문을 추가하는데, 이때 아래와 같이 하나는 초기화 구문을 지원하도록 하고 2개의 required속성을 설정하도록 합니다. 이때 해당 생성자에는&amp;nbsp; [SetsRequiredMembers] attribute를 적용하여 compiler에게 생성자를 통해 required 속성이 설정되고 있음을 명시적으로 알려줘야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706145175653&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public Book() { }

[SetsRequiredMembers]
public Book(string? isbn, string? title)
{
	Isbn = isbn;
	Title = title;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 통해 Program.cs에서는 이전에 Book의 초기화 문대신 아래와 같은 생성자를 통해 어떠한 required속성도 설정할 필요 없이 instance를 생성할 수 있게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706145175653&quot; class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;Book book = new(isbn: &quot;123-4567890000&quot;, title: &quot;The Little Prince&quot;)
{
	Author = &quot;Antoine de Saint-Exup&amp;eacute;ry&quot;,
	PageCount = 100
};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xoTIJ/btsDIBu7E2t/syEXwHIKkdft6yYfRa2fQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xoTIJ/btsDIBu7E2t/syEXwHIKkdft6yYfRa2fQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xoTIJ/btsDIBu7E2t/syEXwHIKkdft6yYfRa2fQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxoTIJ%2FbtsDIBu7E2t%2FsyEXwHIKkdft6yYfRa2fQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;76&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(13) 생성자를 통해 field초기화 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 경우는 field가 runtime에 초기화(값의 설정)가 되어야 할 때가 있는데 이는 new keyword를 통해 class를 intance화 할때 호출되는 생성자로 실현할 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 readonly HomePlanet다음에 두 번째 readonly field인 Instantiated를 정의하고 Name과 Instantiated field에 값을 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706145959620&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public readonly DateTime Instantiated;
public Person()
{
   Name = &quot;Unknown&quot;; 
  Instantiated = DateTime.Now;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 다음 새로운 Person의 intance를 생성하고 초기화된 field의 값을 표시하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706146174973&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person blankPerson = new();
WriteLine(&quot;Name : {Name}, Instantiated : {Instantiated}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;394&quot; data-origin-height=&quot;18&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cF2z1l/btsDWuaL3k9/RZlrdvigykCIFrvCPnqAD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cF2z1l/btsDWuaL3k9/RZlrdvigykCIFrvCPnqAD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cF2z1l/btsDWuaL3k9/RZlrdvigykCIFrvCPnqAD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcF2z1l%2FbtsDWuaL3k9%2FRZlrdvigykCIFrvCPnqAD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;394&quot; height=&quot;18&quot; data-origin-width=&quot;394&quot; data-origin-height=&quot;18&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(14) 다중 생성자 정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 하나의 type에 여러 생성자를 가질 수 있습니다. 이 것은 특히 개발자가 field에 대한 초기값을 설정하도록 권장하는데 유용하게 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 개발자가 Person의 Name과 HomePlanet에 초기값을 설정할 수 있는 두 번째 생성자를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685002359986&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Person(string initialName, string homePlanet)
{
	Name = initialName;
	HomePlanet = homePlanet;
	Instantiated = DateTime.Now;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 또 다른 Person의 Intance를 생성하는데 이때 두 개의 매개변수를 가진 두 번째 생성자를 사용하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685002559490&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person gunny = new(initialName: &quot;Matthew Paige Damon&quot;, homePlanet: &quot;Mars&quot;);
Console.WriteLine(format: &quot;{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.&quot;, arg0: gunny.Name, arg1: gunny.HomePlanet, arg2: gunny.Instantiated);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 실행결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oOKUa/btsDQgJvAvE/E95oqpBgHJ26d2GH5FYXQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oOKUa/btsDQgJvAvE/E95oqpBgHJ26d2GH5FYXQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oOKUa/btsDQgJvAvE/E95oqpBgHJ26d2GH5FYXQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoOKUa%2FbtsDQgJvAvE%2FE95oqpBgHJ26d2GH5FYXQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;74&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자는 다소 특별한 method로 분류될 수 있는데 method와 관련해서는 이어서 좀 더 자세히 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. Method의 호출과 작성&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method는 여러 구문을 실행하는 type의 member라고 할 수 있습니다. 즉 type에 속하는 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 값을 반환하는 method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method는 단일 값을 반환하거나 아무것도 반환하지 않을 수 있습니다. 어떤 동작을 수행하지만 값을 반환하지 않는 경우라면 method의 이름 앞에 void type을 사용해 표시하고 그렇지 않고 값을 반환한다면 method의 이름 앞에 반환하는 값의 type을 사용해 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 예제를 위해 2개의 method를 생성할 예정인데 각각의 method는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WriteToConsole : console에 text를 출력하는 method로서 동작만 수행할 뿐 아무런 값도 반환하지 않을 것이므로 해당 method에는 void keyword를 적용합니다.&lt;/li&gt;
&lt;li&gt;GetOrigin : 해당 method는 text값을 반환하므로 method에 string keyword를 적용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 위 2개의 method를 정의하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685006606930&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void WriteToConsole()
{
	WriteLine($&quot;{Name} was born on a {DateOfBirth:dddd}.&quot;);

}

public string GetOrigin()
{
	return $&quot;{Name} was born on {HomePlanet}.&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서는 위 method를 호출하는 구문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685006700201&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person hong = new()
{
	Name = &quot;honggd&quot;,
	DateOfBirth = new(1982, 09, 29)
};

hong.WriteToConsole();
Console.WriteLine(hong.GetOrigin());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ix9zz/btsDIv23lBr/KR37B29LRe1Mwdc7MJvq30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ix9zz/btsDIv23lBr/KR37B29LRe1Mwdc7MJvq30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ix9zz/btsDIv23lBr/KR37B29LRe1Mwdc7MJvq30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIx9zz%2FbtsDIv23lBr%2FKR37B29LRe1Mwdc7MJvq30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;93&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 매개변수를 정의하고 Method로 전달하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method는 동작하는데 필요한 값을 얻기 위해 전달가능한 매개변수를 가질 수 있습니다. 매개변수는 변수의 선언과 비슷하게 정의될 수 있지만 이전 예제에서의 생성자와 같이 method의 괄호 안에 들어간다는 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 2개의 method를 정의하는데 첫 번째 method는 매개변수를 가지지 않고 두 번째 method는 하나의 매개변수를 가집니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685170485135&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public string SayHello()
{
	return $&quot;{Name} says 'Hello!'&quot;;
}

public string SayHelloTo(string name)
{
	return $&quot;{Name} says 'Hello, {name}!'&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 위 2개의 method를 호출하고 console에 반환값을 출력하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685170660206&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(hong.SayHello());
Console.WriteLine(hong.SayHelloTo(&quot;kim&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1fK6C/btsDKPeQH1o/kZZlaOYEgIzk4LIjQBSk7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1fK6C/btsDKPeQH1o/kZZlaOYEgIzk4LIjQBSk7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1fK6C/btsDKPeQH1o/kZZlaOYEgIzk4LIjQBSk7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1fK6C%2FbtsDKPeQH1o%2FkZZlaOYEgIzk4LIjQBSk7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;115&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method를 호출하는 구문을 typing 할 때 IntelliSense는 tootip으로 이름과 함께 매개변수의 type, 그리고 method의 반환 type을 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Method overload&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름이 다른 2개의 method를 정의하는 대신 우리는 하나의 method에 같은 이름을 부여할 수 있습니다. 이 것은 method가 각각 다른 매개변수를 가지게 함으로써 가능합니다. 그리고 이미 이에 대한 예로서 위에서 생성자를 선언하면서 확인해 본 바가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 SayHelloTo method의 이름을 SayHello로 변경합니다. 그리고 Program.cs에서도 역시 method의 호출 부분을 SayHello로 변경합니다. 이때 tootip을 통해서 해당 method가 1 of 2와 2 of 2를 통해 추가적인 overload가 있음을 다음과 같이 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nUKRM/btsDJllhWCM/flTf6DcR9AgOaC7kzCkxb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nUKRM/btsDJllhWCM/flTf6DcR9AgOaC7kzCkxb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nUKRM/btsDJllhWCM/flTf6DcR9AgOaC7kzCkxb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnUKRM%2FbtsDJllhWCM%2FflTf6DcR9AgOaC7kzCkxb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;426&quot; height=&quot;71&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;overload 된 method를 사용하면 class의 method가 적은 것처럼 나타낼 수 있고 class자체를 더욱 간소화하게 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 선택적 그리고 명명된 매개변수 전달&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method를 간소화하기 위한 또 다른 방법으로는 매개변수를 선택적으로 만드는 것입니다. 이는 method내부의 매개변수에 기본값을 할당함으로써 가능합니다. 이때 선택적 매개변수는 반드시 매개변수의 마지막에 와야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 아래와 같이 3개의 선택적 매개변수를 갖는 method를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685248016366&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public string OptionalParameters(string command = &quot;Run!&quot;, double number = 0.0, bool active = true)
{
	return string.Format(format: &quot;command is {0}, number is {1}, active is {2}&quot;, arg0: command, arg1: number, arg2: active);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서는 method를 호출하고 console에 결과를 표시하도록 하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685248087303&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(hong.OptionalParameters());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때도 역시 IntelliSense는 tooltip을 통해 기본값을 가진 3개의 선택적 매개변수를 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;49&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Q2ja/btsDLLXydIU/QOVPgG1SJBPivKv2TKkYNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Q2ja/btsDLLXydIU/QOVPgG1SJBPivKv2TKkYNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Q2ja/btsDLLXydIU/QOVPgG1SJBPivKv2TKkYNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Q2ja%2FbtsDLLXydIU%2FQOVPgG1SJBPivKv2TKkYNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;725&quot; height=&quot;49&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;49&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dajnfC/btsDJfrRC3v/rGEAMGMOYr1Dk2qNTkTqzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dajnfC/btsDJfrRC3v/rGEAMGMOYr1Dk2qNTkTqzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dajnfC/btsDJfrRC3v/rGEAMGMOYr1Dk2qNTkTqzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdajnfC%2FbtsDJfrRC3v%2FrGEAMGMOYr1Dk2qNTkTqzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Program.cs에서 command 매개변수에 string을 number 매개변수에 double값을 아래와 같이 전달합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685248809823&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(hong.OptionalParameters(&quot;Jump!&quot;, 95.5));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 예제를 실행하면 다음의 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doSRDF/btsDHphsNzV/K5QSCtuA0C1ykHZocw2vz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doSRDF/btsDHphsNzV/K5QSCtuA0C1ykHZocw2vz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doSRDF/btsDHphsNzV/K5QSCtuA0C1ykHZocw2vz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoSRDF%2FbtsDHphsNzV%2FK5QSCtuA0C1ykHZocw2vz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;command의 기본값과 number 매개변수의 기본값은 전달한 값으로 바뀌었으나 active의 기본값은 여전히 true를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Method 호출 시 매개변수 명명하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명명매개변수는 선언한 순서에 따라 값이 다른 순서로 전달될 수 있도록 하기 때문에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;method를 호출할 때 종종 선택적 매개변수와 결합하여 사용하기도 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Program.cs에서 command 매개변수에 string값을, number 매개변수에 double값을 전달하도록 합니다. 이때, 명명된 매개변수를 사용하는데 순서를 이전과 다르게 지정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685249417390&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(hong.OptionalParameters(number: 52.7, command: &quot;Hide!&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 결과는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 명명된 매개변수를 사용하면 일부 선택적 매개변수를 생략하는 데에도 사용할 수 있습니다. Program.cs에서 선택적 순서를 통해 command 매개변수로 string값을 전달하고 number 매개변수는 생략합니다. 또한 action 매개변수는 명명된 매개변수를 사용하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685249849079&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(hong.OptionalParameters(&quot;Pooling!&quot;, active: false));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ufzgN/btsDJVzSr46/ilFIuKaEtMwdPLCfRqcKeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ufzgN/btsDJVzSr46/ilFIuKaEtMwdPLCfRqcKeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ufzgN/btsDJVzSr46/ilFIuKaEtMwdPLCfRqcKeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FufzgN%2FbtsDJVzSr46%2FilFIuKaEtMwdPLCfRqcKeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 매개변수 전달 방식 제어하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수가 method로 전달될 때는 아래 3가지 중 하나의 방식이 사용될 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값 (기본방식) : 입력전용으로 처리됩니다.&lt;/li&gt;
&lt;li&gt;out 매개변수 : 출력전용으로 처리되며 매개변수 선언과 함께 기본값을 지정할 수 없고 초기화되지 않은 채로 남아있을 수 없습니다. 따라서 반드시 method내부에서 값이 설정되어야 합니다. 그렇지 않으면 compiler는 error를 발생시키게 될 것입니다.&lt;/li&gt;
&lt;li&gt;ref 매개변수 : 입력과 출력으로 동시에 사용될 수 있습니다. out 매개변수와 같이 ref 매개변수 또한 기본값을 가질 수 없으며 이미 method외부에서 값이 설정되어 있기 때문에 method내부에서 반드시 값을 설정하는 처리를 필요로 하지 않습니다.&lt;/li&gt;
&lt;li&gt;in 매개변수 : 매개변수의 값이 변경될 수 없으며 그러한 행위가 시도되면 compiler는 error를 발생시키게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력과 출력으로 매개변수를 전달하는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예제를 만들어 보기 위해&amp;nbsp;&lt;/span&gt;Person.cs에서 4개의 매개변수를 가진 method를 정의합니다. 이 중 하나는 값매개변수이고 다른 하나는 in, ref, 마지막 하나는 out 매개변수입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685321353219&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void PassingParameters(int w, in int x, ref int y, out int z)
{
  z = 100;
  y++;
  //x++; x는 in입니다. 값을 변경하려 한다면 error가 발생합니다.
  w++;

  Console.WriteLine($&quot;In the PassingParameters: w={w}, x={x}, y={y}, z={z}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 3개의 int변수를 선언하고 이들을 method로 전달합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685322200809&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int a = 10;
int b = 20;
int c = 30;
int d = 40;

Console.WriteLine($&quot;Before: a = {a}, b = {b}, c = {c}, d = {d}&quot;);
PassingParameters(a, b, ref c, out d);
Console.WriteLine($&quot;After: a = {a}, b = {b}, c = {c}, d = {d}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 다음과 같은 결과를 확인합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Before:&amp;nbsp;a&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;b&amp;nbsp;=&amp;nbsp;20,&amp;nbsp;c&amp;nbsp;=&amp;nbsp;30,&amp;nbsp;d&amp;nbsp;=&amp;nbsp;40 &lt;br /&gt;In&amp;nbsp;the&amp;nbsp;PassingParameters:&amp;nbsp;w=11,&amp;nbsp;x=20,&amp;nbsp;y=31,&amp;nbsp;z=100 &lt;br /&gt;After:&amp;nbsp;a&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;b&amp;nbsp;=&amp;nbsp;20,&amp;nbsp;c&amp;nbsp;=&amp;nbsp;31,&amp;nbsp;d&amp;nbsp;=&amp;nbsp;100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 매개변수로 변수를 전달할 때는 변수 자체가 아닌 전달된 현재 값을 가져오게 됩니다. 따라서 w는 변숫값의 복사본을 가지게 되고 변수는 본래값 10을 계속 유지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;in 매개변수가 사용되는 경우에는 해당 변수에 대한 참조가 method로 전달됩니다. 그러므로 x는 b를 참조하게 됩니다. x는 PassingParameters method안에서 변경될 수 없지만 method밖에서 b의 값은 바뀔 수 있고 그러면 PassingParameters method에도 바뀐 값이 적용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ref 매개변수로 변수를 전달하는 경우에는 method로 전달된 변수의 참조를 가져오게 됩니다. 따라서 y는 c에 대한 참조가 됩니다. c변수가 증가된 값을 가지게 되면 y 매개변수 역시 증가된 값을 가지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;out 매개변수로 변수를 전달하는 경우에는 method로 전달된 변수의 참조를 가져오게 됩니다. 따라서 z는 d에 대한 참조가 되고 d변숫값은 method안에서 실행되는 code에 의해 바뀌게 됩니다. 예제에서는 Before로의 출력을 위해 d변수에 값을 할당했지만 사실 이 부분만 아니면 변수 d에 값을 할당하지 않아도 됩니다. 해당 변수는 어쨌거나 다른 값으로 바뀌기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● out 매개변수 간소화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 7부터는 out 매개변수를 사용하는 code를 더욱 간소화할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 변수를 더 선언하고 f이름의 inline선언 out 매개변수를 추가합니다. f는 본래 존재하지 않던 변수지만 out 매개변수를 통해 method에서 처리되면서 해당 변수를 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685323511966&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int a = 10;
int b = 20;
int c = 30;

Console.WriteLine($&quot;Before: a = {a}, b = {b}, c = {c}&quot;);
PassingParameters(a, b, ref c, out int d);
Console.WriteLine($&quot;After: a = {a}, b = {b}, c = {c}, d = {d}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 결과는 다음과 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Before:&amp;nbsp;a&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;b&amp;nbsp;=&amp;nbsp;20,&amp;nbsp;c&amp;nbsp;=&amp;nbsp;30 &lt;br /&gt;In&amp;nbsp;the&amp;nbsp;PassingParameters:&amp;nbsp;w=11,&amp;nbsp;x=20,&amp;nbsp;y=31,&amp;nbsp;z=100 &lt;br /&gt;After:&amp;nbsp;a&amp;nbsp;=&amp;nbsp;10,&amp;nbsp;b&amp;nbsp;=&amp;nbsp;20,&amp;nbsp;c&amp;nbsp;=&amp;nbsp;31,&amp;nbsp;d&amp;nbsp;=&amp;nbsp;100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) ref return&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 7부터 ref keyword는 method로의 매개변수 전달뿐만 아니라 return 값에도 적용될 수 있습니다. 결과를 반환할 때 실제 값이 아닌 참조를 반환하도록 함으로써 외부 변수가 내부 변수를 참조하고 method가 호출된 후 해당 값이 수정될 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 하나의 array변수와 특정 index의 참조를 반환하는 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685324682420&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private int[] position = new int[3];
public ref int GetValue(int index)
{
	return ref position[index];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 해당 method를 호출하는 2개의 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685324717723&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ref int v = ref hong.GetValue(1);

v = 10;

Console.WriteLine(hong.GetValue(1));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 실행결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;77&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFkBWi/btsDLrrhuG0/NOxdhLYbikeI8vL7hBcwik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFkBWi/btsDLrrhuG0/NOxdhLYbikeI8vL7hBcwik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFkBWi/btsDLrrhuG0/NOxdhLYbikeI8vL7hBcwik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFkBWi%2FbtsDLrrhuG0%2FNOxdhLYbikeI8vL7hBcwik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;77&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;77&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제는 method내부 변수의 참조를 통해 외부에서 값을 변경하고 다시 method안에서 해당 값을 확인하고 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) tuple을 사용한 다중 값 반환하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Method는 기본적으로 하나의 type에 대한 하나의 값만을 반환할 수 있습니다. 여기서 type은 이전 예제와 같이 string이 될 수 있고 Person과 같이 복합 type이 될 수도 있습니다. 혹은 List&amp;lt;Person&amp;gt;과 같이 collection type을 반환하는 것도 가능합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 만약 GetTheData이름의 method를 만들어야 하는데 해당 method에서 string값과 int값을 둘 다 받아야 하는 경우라면 어떻게 할 수 있을까? 쉽게는 TextAndNumber와 같은 string field와 int field를 갖는 새로운 class를 정의하고 해당 type의 intance를 반환하도록 할 수 있을 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916453&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public class TextAndNumber
{
	public string Text;
	public int Number;
}
public class Human
{
	public TextAndNumber GetTheData()
	{
		return new TextAndNumber
		{
			Text = &quot;Hong&quot;,
			Number = 25
		};
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 둘의 값을 결합하기 위해 꼭 class를 정의할 필요는 없습니다. C#은 이런 경우에 사용가능한 tuple이라는 것을 사용할 수 있는데 Tuple은 2개 혹은 그 이상의 값을 하나의 단위로 결합하기 위한 효율적인 방식을 제공합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Tuple은 F#과 같은 어떤 언어에서는 첫 version부터 지원하기 시작했지만 .NET에서는 2010년 .NET 4.0부터 System.Tuple type을 통해 tuple을 지원하기 시작했습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● C# 언어에서의 tuple지원&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;C#이 괄호(())를 사용해 tuple을 위한 언어 구문을 지원하는 것은 2017년 C# 7.0에서만 가능했습니다. 같은 시간 .NET은 일부 일반적인 scenaris에서 old version의 .NET 4.0 System.Tuple type보다 더 효율적인 새로운 System.VlueTuple type추가하였으며 이것으로 더욱 효율적인 구문을 사용할 수 있게 되었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Person.cs에서는 string과 int를 결합한 tuple을 반환하는 method를 정의하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916455&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public (string, int) GetFruit()
{
	return (&quot;Apples&quot;, 5);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 위의 GetFruit method를 호출하고 tuple의 field를 출력하는 구문을 추가합니다. 이때 field는 Item1과 Item2라는 이름이 자동적으로 부여됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916455&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;(string, int) fruit = hong.GetFruit();
Console.WriteLine($&quot;{fruit.Item1} - {fruit.Item2}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deegPo/btsDGK0m1xe/EPJ3ZaN0WG0Iy8olODOiV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deegPo/btsDGK0m1xe/EPJ3ZaN0WG0Iy8olODOiV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deegPo/btsDGK0m1xe/EPJ3ZaN0WG0Iy8olODOiV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeegPo%2FbtsDGK0m1xe%2FEPJ3ZaN0WG0Iy8olODOiV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;73&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Tuple field의 Naming&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;tuple의 field에 접근하기 위해서는 기본적으로 Item1, Item2등의 이름을 사용할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 명시적으로 각 field에 이름을 부여하는 것도 가능합니다. Person.cs에서 field의 이름과 함께 tuple을 반환하는 method를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916456&quot; class=&quot;fortran&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public (string Name, int Number) GetNamedFruit()
{
	return (Name: &quot;Apples&quot;, Number: 5);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Progam.cs에서는 method를 호출하는 구문을 추가하고 tuple의 field를 이름을 사용하여 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916457&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;var fruitNamed = hong.GetNamedFruit();
Console.WriteLine($&quot;There are {fruitNamed.Number} {fruitNamed.Name}.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 예제는 다음과 같은 형태로도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706231999700&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(int Number, string Name) fruitNamed = bob.GetNamedFruit();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rq4rN/btsDJC1ouGR/S5MnArkgkrb0uknU5nyWqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rq4rN/btsDJC1ouGR/S5MnArkgkrb0uknU5nyWqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rq4rN/btsDJC1ouGR/S5MnArkgkrb0uknU5nyWqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frq4rN%2FbtsDJC1ouGR%2FS5MnArkgkrb0uknU5nyWqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;75&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 다른 개체에서 생성된 tuple이라면 C# 7.1에서 도입된 tuple name inference(tuple 이름 추론)이라는 기능을 사용할 수도 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다시 Program.cs에서 string과 int값으로 구성된 2개의 tuple을 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916458&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;var thing1 = (&quot;kim&quot;, 3);
Console.WriteLine($&quot;{thing1.Item1} has {thing1.Item2} children.&quot;);

var thing2 = (hong.Name, hong.Children.Count);
Console.WriteLine($&quot;{thing2.Name} has {thing2.Count} children.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;C# 7.0에서는 위 2가지 모두 Item1과 Item2라는 이름 체계를 사용합니다. 또한 C# 7.1부터는 thing2는 Name과 Count라는 이름을 추론할 수도 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Tuple 분해&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Tuple은 또한 개별적인 변수로 분해될 수 있습니다. 이 방식은 명명된 field tuple과 같은 문법으로 구현되지만 tuple에 대한 변수가 없습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916459&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;//2개의 field를 가진 tupe변수로 받는것이 아닌
(string TheName, int TheNumber) tupleWithNamedFields = hong.GetNamedFruit();

//2개의 변수로 받음
(string Name, int Number) = hong.GetNamedFruit();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 함으로써 tuple자체를 각각의 새로운 변수로 분해하게 됩니다. Program.cs에서 GetFruit method로 반환된 tuple을 분해하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916459&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;(string Name, int Number) = hong.GetFruit();

Console.WriteLine($&quot;Deconstructed: {Name}, {Number}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Tuple 별칭&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Tuple을 별칭화 하는 건 C#12에 도입된 것으로 type을 명명하고 이를 변수를 선언할 때 type과 pamater의 이름으로서 사용할 수 있도록 하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706486584506&quot; class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnnamedParameters = (string, int);

using NamedParamters = (string Name, int Number);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;참고로 Tuple 별칭을 사용할 때는 title case(pascal case)를 사용합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같은 방식으로 tuple type을 선언하면 다음과 같이 매개변수 type이 동일한 경우 해당 tuple type으로 값을 받을 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706486584507&quot; class=&quot;autoit&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using NamedParamters = (string Name, int Number);

(string Name, int Number) = hong.GetNamedFruit();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Type 해체&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;tuple만이 분해될 수 있는 유일한 type이 아닙니다. 모든 type은 개체를 부분적으로 분해할 수 있는 Deconstruct란 특별한 method를 가지고 있습니다. Person.cs에서는 부분적으로 나누고자 하는 out 매개변수가 정의된 2가지 Deconstruct method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916460&quot; class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;public void Deconstruct(out string? name, out DateTime dob)
{
	name = Name;
	dob = DateOfBirth;
}

public void Deconstruct(out string? name, out DateTime dob, out WondersOfTheAncientWorld fav)
{
	name = Name;
	dob = DateOfBirth;
	fav = FavoriteAncientWonder;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;예제에서 사용된 out keyword에 관해서는 추후에 자세히 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 hong을 분해하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706149916461&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;var (name1, dob1) = hong;
Console.WriteLine($&quot;Deconstructed: {name1}, {dob1}&quot;);

var (name2, dob2, fav2) = hong;
Console.WriteLine($&quot;Deconstructed: {name2}, {dob2}, {fav2}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;개체를 tuple변수에 할당할 때 암시적으로 호출되므로 예제와 같이 Deconstruct method를 명시적으로 호출하지 않습니다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 예제를 실행하면 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o1bBW/btsDGLLOpek/poCICQ1bVnuLm2K2LBmvc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o1bBW/btsDGLLOpek/poCICQ1bVnuLm2K2LBmvc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o1bBW/btsDGLLOpek/poCICQ1bVnuLm2K2LBmvc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo1bBW%2FbtsDGLLOpek%2FpoCICQ1bVnuLm2K2LBmvc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;115&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. partial을 사용한 class분리&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 team의 member로 구성된 대규모 project를 구축하는 경우 또는 크고 복잡한 class를 구현할 때는 여러 file에 걸쳐 class를 나누어 정의하는 것이 필요할 수 있는데 이것은 partial keyword를 통해 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나 예를 들어 database로부터 schema 정보를 읽는 개체-관계(object-relational) mapper와 같은 도구에 의해 자동적으로 생성된 Person class에 어떤 구문을 추가해야 한다면 해당 class가 partial로 정의되어 있을 때 우리는 class를 자동으로 생성된 code file과 임의로 작성된 code로 나눌 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상황에 있다고 가정했을 때 Person class를 어떻게 작성할 수 있을지 예제를 통해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person.cs에서 아래와 같이 partial keyword를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685338788421&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public partial class Person : object&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 뒤 NetLibraryStandard2 project에 PersonAutoGen.cs라는 이름의 file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685338916006&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace CLIEL.Shared
{
	public partial class Person
	{
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 Person.cs에 작성될 code는 이제 PersonAutoGen.cs에서 작성할 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. Property와 Indexer를 통한 접근 제어&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 예제에서는 Person의 origin과 name을 포함한 string을 반환하는 GetOrigin method를 생성했었습니다. Java와 같은 언어에서는 이와 같은 것을 많이 하는데 C#은 property라는 더 나은 방법을 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;property는 단순화된 method(또는 method의 쌍)으로서 값을 가져오거나 설정할 때 어떤 동작을 수행하며 field처럼 값을 확인할 수 있는데, 비교적 간단한 구문을 통해 이를 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 읽기 전용 property 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 전용 property는 단지 get만 구현된 것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PersonAutoGen.cs file의 Person class에서 아래 3개의 속성을 정의합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 속성은 C#의 모든 version에서 작동하는 property 구문을 사용한 GetOrigin method와 동일한 역할을 수행합니다.&lt;/li&gt;
&lt;li&gt;두 번째 속성은 C# 6부터 사용가능한 lambda 표현식 본문인 =&amp;gt; 구문을 사용해 환영 message를 반환합니다.&lt;/li&gt;
&lt;li&gt;세 번째 속성은 person에 대한 age를 계산합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1685345459851&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public partial class Person
{
	public string Origin
	{
		get
		{
			return string.Format(&quot;{0} was born on {1}&quot;, arg0: Name, arg1: HomePlanet);
		}
	}

	public string Greeting =&amp;gt; $&quot;{Name} says 'Hello!'&quot;;
	public int Age =&amp;gt; DateTime.Today.Year - DateOfBirth.Year;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 위 속성에 대한 값을 가져오는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685345562737&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person hong = new()
{
	Name = &quot;honggd&quot;,
	DateOfBirth = new(1982, 09, 29)
};

Console.WriteLine(hong.Origin);
Console.WriteLine(hong.Greeting);
Console.WriteLine(hong.Age);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dn2cnr/btsDKQkyy1b/ZBDFc4inUlCng12SPq6qM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dn2cnr/btsDKQkyy1b/ZBDFc4inUlCng12SPq6qM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dn2cnr/btsDKQkyy1b/ZBDFc4inUlCng12SPq6qM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdn2cnr%2FbtsDKQkyy1b%2FZBDFc4inUlCng12SPq6qM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;93&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 설정 가능 property 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정가능한 속성을 생성하려면 get뿐만 아니라 set부분 method를 같이 한쌍으로 제공해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PersonAutoGen.cs에서 get과 set method(getter와 setter)를 가진 string 속성을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685346283144&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public string? FavoriteIceCream { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 Person의 좋아하는 ice cream을 저장하기 위한 field를 정의하지 않았지만 위와 같은 code만으로 compiler는 자동적으로 필요한 field를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 속성의 값이 설정될 때 더 세세한 제어가 필요할 수 있는데 이런 경우에는 해당 기능에 필요한 구문과 함께 속성에 값을 저장하기 위한 private field를 임의로 생성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PersonAutoGen.cs에서 string field와 get과 set을 가진 string 속성을 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685346619033&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private string? favoritePrimaryColor;
public string? FavoritePrimaryColor
{
	get
	{
		return favoritePrimaryColor;
	}

	set
	{
		switch (value?.ToLower())
		{
			case &quot;red&quot;:
			case &quot;green&quot;:
			case &quot;blue&quot;:
				favoritePrimaryColor = value;
				break;
			default:
				throw new ArgumentException($&quot;{value} is not a primary color. Choose from: red, green, blue.&quot;);

		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;되도록이면 getter와 setter에 너무 많은 code가 들어가지 않도록 해야 합니다. code가 너무 긴 경우에는 private method를 추가하여 getter와 setter에서 호출하도록 해 code를 간소화하는 것이 좋습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 person의 FavoriteIceCream과 FavoritePrimaryColor를 설정하는 구문을 추가하고 설정값을 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685346914103&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hong.FavoriteIceCream = &quot;Vanilla&quot;;
Console.WriteLine($&quot;hong's favorite ice-cream flavor is {hong.FavoriteIceCream}.&quot;);

string color = &quot;Red&quot;;
try
{
	hong.FavoritePrimaryColor = color;
	Console.WriteLine($&quot;hong's favorite primary color is {hong.FavoritePrimaryColor}.&quot;);
}
catch (Exception ex)
{
	Console.WriteLine(&quot;Tried to set {0} to '{1}': {2}&quot;, nameof(hong.FavoritePrimaryColor), color, ex.Message);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tFaeq/btsDLuO38ky/wVtzXsVCoYK6g0p0hSlTbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tFaeq/btsDLuO38ky/wVtzXsVCoYK6g0p0hSlTbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tFaeq/btsDLuO38ky/wVtzXsVCoYK6g0p0hSlTbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtFaeq%2FbtsDLuO38ky%2FwVtzXsVCoYK6g0p0hSlTbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;95&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;color에는 red, green, blue 이외에 다른 값의 설정을 시도하면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nl69p/btsDHmycKVr/uOKMnmVfPbiOJfObwowMU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nl69p/btsDHmycKVr/uOKMnmVfPbiOJfObwowMU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nl69p/btsDHmycKVr/uOKMnmVfPbiOJfObwowMU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnl69p%2FbtsDHmycKVr%2FuOKMnmVfPbiOJfObwowMU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;81&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;field에 대한 값을 설정하고 읽을 때 되도록이면 GetAge나 SetAge와 같은 method의 사용을 피하고 속성을 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Indexer 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Indexer는 호출 code가 배열문법을 통해 속성으로 접근할 수 있도록 합니다. 예를 들어 string type에서는 indexer를 정의하고 있으므로 호출 code가 string에 있는 각각의 문자에 아래와 같이 접근할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685427648155&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string alphabet = &quot;abcdefghijklmnopqrstuvwxyz&quot;;
char letterF = alphabet[5]; // f&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person에서도 children으로의 접근을 간소화하기 위해 indexer를 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PersonAutoGen.cs에서 child의 index를 통해 child를 설정하고 가져올 수 있는 indexer를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685428035034&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Person this[int index]
{
	get { return Children[index]; }
	set { Children[index] = value; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 indexer의 재정의를 통해 int값을 전달하는 것 외에 매개변수에 string 같은 다른 type을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PersonAutoGen.cs에서 child의 이름을 사용해 child를 설정하고 가져올 수 있는 indexer를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685428183210&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Person this[string name]
{
	get
	{
		return Children.Find(p =&amp;gt; p.Name == name);
	}
	set
	{
		Person found = Children.Find(p =&amp;gt; p.Name == name);

		if (found is not null)
			found = value;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;List&amp;lt;T&amp;gt;와 같은 collection과 lambda 식에 관해서는 추후에 자세히 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 hong에 대한 2개의 children을 추가하고 각각의 방식을 통해 Child에 접근하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685428705081&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hong.Children.Add(new()
{
	Name = &quot;gil&quot;,
	DateOfBirth = new(2014, 6, 28)
});

hong.Children.Add(new()
{
	Name = &quot;young&quot;,
	DateOfBirth = new(2015, 10, 11)
});

//Children List 사용
Console.WriteLine($&quot;Hong's first child is {hong.Children[0].Name}.&quot;);
Console.WriteLine($&quot;Hong's second child is {hong.Children[1].Name}.&quot;);

//integer position indexer 사용
Console.WriteLine($&quot;Hong's first child is {hong[0].Name}.&quot;);
Console.WriteLine($&quot;Hong's second child is {hong[1].Name}.&quot;);

// indexer명 사용
Console.WriteLine($&quot;Hong's child named Ella is {hong[&quot;gil&quot;].Age} years old.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ezK3Mf/btsDP9XUYjI/rLmlh8OLoWOaJKccSTHRaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ezK3Mf/btsDP9XUYjI/rLmlh8OLoWOaJKccSTHRaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ezK3Mf/btsDP9XUYjI/rLmlh8OLoWOaJKccSTHRaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FezK3Mf%2FbtsDP9XUYjI%2FrLmlh8OLoWOaJKccSTHRaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;119&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;7. Method 확장하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 method의 활용은 이전에 이미 살펴본 바 있습니다. 자주 사용되는 것은 아니지만 method의 좀 더 효율적인 활용을 위해 추가적인 몇몇 사항들을 더 알아보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+나 *와 같은 연산자는 숫자에만 적용될 수 있는 것은 아닙니다. 예를 들어 Person의 instance에 적용되어 +나 *와 같은 연산자에 동작할 수 있는 method를 생성하고 연산자를 재정의함으로써 2개의 Person에 +연산자를 적용하는 경우 결혼을, *연산자를 적용하는 경우 아이의 출산과 같은 동작을 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance method는 개체 자체에서 동작하는 것이며 static(정적) method는 type에서 동작하는 것입니다. 어떤 것을 선택할지는 동작에 가장 접합한 것이 무엇이냐에 따라 달라집니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;비슷한 동작을 수행하기 위한 정적 method와 instance method를 둘 다 정의하는 것은 때로는 현명한 수단이 될 수 있습니다. 예를 들어 string은 Compare라는 정적 method와 CompareTo라는 instance method를 가지고 있으며 개발자는 편의에 따라 어떤 것을 사용할지 선택할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Method를 사용한 기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 static과 instance method를 사용한 기능을 구현해 볼 것입니다. PersonAutoGen.cs의 Person class에서 private field를 가진 읽기 전용 property를 추가하여 결혼을 했는지, 했으면 누구랑 했는지를 나타내도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685491152354&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private bool married = false;
public bool Married =&amp;gt; married;

private Person? spouse = null;
public Person? Spouse =&amp;gt; spouse;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 하나의 instance method와 static method를 Person class에 추가하여 2개의 Person개체로 결혼할 수 있는 기능을 부여합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685491324138&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void Marry(Person p1, Person p2)
{
	p1.Marry(p2);
}

//instance method
public void Marry(Person partner)
{
	if (married) return;

	spouse = partner;
	married = true;

	partner.Marry(this);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 다음 2가지 사항에 주목해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Marry static method에서 Person개체는 p1과 p2 매개변수로 Marry instance method로 전달됩니다.&lt;/li&gt;
&lt;li&gt;Marry instance method에서는 매개변수로 전달된 partner로 spouse가 설정되며 married bool 변수가 true로 설정됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 2개의 Person개체가 아이를 낳을 수 있도록 하는 instance와 static method를 하나씩 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685491876089&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Person Procreate(Person p1, Person p2)
{
	if (p1.Spouse != p2)
	{
		throw new ArgumentException(&quot;일단 결혼부터...&quot;);
	}

	Person baby = new()
	{
		Name = $&quot;Baby of {p1.Name} and {p2.Name}&quot;,
		DateOfBirth = DateTime.Now
	};

	p1.Children.Add(baby);
	p2.Children.Add(baby);

	return baby;
}

public Person ProcreateWith(Person partner)
{
	return Procreate(this, partner);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 아래 사항에 주목합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Procreate static method에서 아이를 낳을 Person 개체는 매개변수 p1과 p2로 전달됩니다.&lt;/li&gt;
&lt;li&gt;baby라는 새로운 Person class는 부모가 될 둘의 Person에 대한 이름의 결합으로 만들어지게 됩니다. 그러나 이렇게 설정된 이후에 반환된 baby 변수의 Name속성을 설정하게 됨으로써 해당 값은 바뀔 수 있습니다.&lt;/li&gt;
&lt;li&gt;baby개체는 둘 부모의 Children collection으로 추가되고 반환됩니다. Class는 참조 type이므로 이때는 baby 개체의 복사본이 아닌 memory에 저장된 baby 개체의 참조가 추가됩니다. 참조 type과 값 type에 대한 자세한 차이점은 추후에 알아볼 것입니다.&lt;/li&gt;
&lt;li&gt;ProcreateWith instance method에서 아이를 낳을 Person개체는 자신을 나타내며 Procreate static method로 전달된 this와 함께 partner이름의 매개변수로 전달하여 구현된 method를 재사용합니다. this는 class의 현재 instance를 참조하는 keyword입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;새로운 개체를 생성하거나 기존 개체를 수정하는 method는 개체에 대한 참조를 반환하도록 함으로써 method의 호출자가 결과에 접근할 수 있어야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 3개의 Person개체를 생성하고 이들에 대한 결혼을 수행한 뒤 서로 아이들 낳을 수 있도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685494386287&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person kim = new() { Name = &quot;Kim&quot; };
Person lee = new() { Name = &quot;Lee&quot; };
Person hong = new() { Name = &quot;Hong&quot; };

kim.Marry(lee);
Person.Marry(hong, kim);

Console.WriteLine($&quot;{kim.Name} is married to {kim.Spouse?.Name ?? &quot;nobody&quot;}&quot;);
Console.WriteLine($&quot;{lee.Name} is married to {lee.Spouse?.Name ?? &quot;nobody&quot;}&quot;);
Console.WriteLine($&quot;{hong.Name} is married to {hong.Spouse?.Name ?? &quot;nobody&quot;}&quot;);

Person baby1 = kim.ProcreateWith(lee);
baby1.Name = &quot;Baby1&quot;;
Console.WriteLine($&quot;{baby1.Name} was born on {baby1.DateOfBirth}&quot;);

Person baby2 = Person.Procreate(hong, kim);
baby2.Name = &quot;Baby2&quot;;
Console.WriteLine($&quot;{kim.Name} has {kim.Children.Count} children.&quot;);
Console.WriteLine($&quot;{lee.Name} has {lee.Children.Count} children.&quot;);
Console.WriteLine($&quot;{hong.Name} has {hong.Children.Count} children.&quot;);

for (int i = 0; i &amp;lt; kim.Children.Count; i++)
{
	Console.WriteLine(format: &quot;{0}'s child #{1} is named \&quot;{2}\&quot;.&quot;, arg0: kim.Name, arg1: i, arg2: kim[i].Name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 마지막 부분에서는 문자열에 큰따옴표(&quot;)를 추가하기 위해 backslash문자를 사용했습니다. 예제를 실행하면 다음과 같은 결과를 나타낼 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBexTQ/btsDQex9HoK/otbseTqbmkr6gNk1jEOJNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBexTQ/btsDQex9HoK/otbseTqbmkr6gNk1jEOJNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBexTQ/btsDQex9HoK/otbseTqbmkr6gNk1jEOJNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBexTQ%2FbtsDQex9HoK%2FotbseTqbmkr6gNk1jEOJNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;169&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 연산자를 사용한 기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.String class에는 2개의 문자열 값을 연결하고 그 결과를 반환하는 Concat이라는 static method가 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685519619304&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s1 = &quot;Hello &quot;;
string s2 = &quot;World!&quot;;
string s3 = string.Concat(s1, s2);
Console.WriteLine(s3);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concat과 같은 method를 통해서도 가능하지만 아무래도 동일한 처리를 위해 가장 많이 사용되는 것은 +연산자일 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685519707072&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s3 = s1 + s2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;이를 통해 code에서 * 문자를 통해 2개의 Person 개체에 대한 출산을, 그리고 + 문자로는 혼인을 구현해 볼 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;앞서 상술한 동작을 위해서는 * 문자를 위한 static 연산자를 정의해야 합니다. 이 문법은 method와 비슷하지만 method의 이름대신 기호를 사용하기에 사실상 연산자가 method이며 이러한 방식으로 문법을 더욱 간소화하게 만들 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;PersonAutoGen.cs에서 + 기호를 위한 static 연산자를 아래와 같이 생성합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685520218608&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static bool operator +(Person p1, Person p2)
{
	Marry(p1, p2);

	return p1.Married &amp;amp;&amp;amp; p2.Married;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;연산자의 return type은 매개변수로 연산자에 전달된 type와 일치할 필요는 없으나 return type이 void가 될 수는 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 * 기호를 위한 static 연산자 또한 아래와 같이 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685520341855&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Person operator *(Person p1, Person p2)
{
	return Procreate(p1, p2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;method와 달리 연산자는 IntelliSense를 통해 type의 list에 나타나지 않습니다. 따라서 정의한 모든 연산자에 대한 method를 같이 만드는 것이 좋습니다. 왜냐하면 연산자를 사용할 수 있다는 것이 개발자에게는 분명하지 않을 수 있기 때문이며 연산자의 구현 부분에서는 method를 호출하여 작성한 code를 재사용합니다. method를 제공해야 하는 두 번째 이유는 연산자가 모든 언어의 compiler에서 지원하는 것이 아니기 때문입니다. 예를 들어 *와 같은 산술연산자가 비록 Visual Basic과 F#에서 지원되기는 하지만 다른 언어가 C#에서 지원되는 모든 연산자를 지원한다는 보장이 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 hong과 kim의 혼인을 위해 호출한 static Marry method부분을 주석처리하고 Marry의 기능을 대체할 +연산자를 사용하여 if문에 넣고 code를 아래와 같이 바꿔줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685521839135&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (kim + hong)
{
	Console.WriteLine($&quot;{kim.Name} and {hong.Name} successfully got married.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 hong의 children을 순회하는 for문 위에서 아래와 같이 * 연산자를 사용해 kim과 그의 아내 lee와 hong사이에 두 명의 아이를 생성하는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685524990582&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person baby3 = kim * lee;
baby3.Name = &quot;Baby3&quot;;
Person baby4 = hong * kim;
baby4.Name = &quot;Baby4&quot;;

for (int i = 0; i &amp;lt; kim.Children.Count; i++)
{
	Console.WriteLine(format: &quot;{0}'s child #{1} is named \&quot;{2}\&quot;.&quot;, arg0: kim.Name, arg1: i, arg2: kim[i].Name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhTI3X/btsDI8GxMIp/hLbN3l0j0u9KnwfNwS0Ab0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhTI3X/btsDI8GxMIp/hLbN3l0j0u9KnwfNwS0Ab0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhTI3X/btsDI8GxMIp/hLbN3l0j0u9KnwfNwS0Ab0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhTI3X%2FbtsDI8GxMIp%2FhLbN3l0j0u9KnwfNwS0Ab0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;251&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 지역함수를 사용한 기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 7.0에서 도입된 언어기능으로 지역함수를 정의하는 것이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역함수는 지역변수와 동일한 method로서 지역함수가 정의된 method안에서만 접근 가능한 method입니다. 다른 언어에서는 때로 중첩된(nested) 또는 inner 함수라고도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역 함수는 method안이면 위, 아래, 중간 어디서든 정의될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 수열 계산을 구현하기 위한 지역함수를 사용할 것이며 이를 위해 PersonAutoGen.cs에서 스스로 결과를 계산하기 위해 내부에서 지역함수를 사용하는 Factorial 함수를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685526495555&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static int Factorial(int number)
{
	if (number &amp;lt; 0)
	{
		throw new ArgumentException($&quot;{nameof(number)} cannot be less than zero.&quot;);
	}

	return localFactorial(number);

	int localFactorial(int localNumber)
	{
		if (localNumber == 0) return 1;
		return localNumber * localFactorial(localNumber - 1);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 Factorial함수를 호출하고 결과값을 console에 출력하는 구문을 예외처리와 함께 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685526599588&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int number = 5;

try
{
	Console.WriteLine($&quot;{number}! is {Person.Factorial(number)}&quot;); 
}
catch (Exception ex)
{
	Console.WriteLine($&quot;{ex.GetType()} says: {ex.Message} number was {number}.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9675l/btsDQeSsylX/iDKk2BKgaFfBXDMpS1XjJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9675l/btsDQeSsylX/iDKk2BKgaFfBXDMpS1XjJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9675l/btsDQeSsylX/iDKk2BKgaFfBXDMpS1XjJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9675l%2FbtsDQeSsylX%2FiDKk2BKgaFfBXDMpS1XjJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;73&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;number변수의 값을 -1로 변경하면 다음과 같은 예외를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JPIFh/btsDJS4cFFJ/uuigIdv3XKkMjaWHKpAj31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JPIFh/btsDJS4cFFJ/uuigIdv3XKkMjaWHKpAj31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JPIFh/btsDJS4cFFJ/uuigIdv3XKkMjaWHKpAj31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJPIFh%2FbtsDJS4cFFJ%2FuuigIdv3XKkMjaWHKpAj31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;76&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;8. 개체에 대한 pattern matching&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글을 통해 우리는 이미 기본적인 pattern matching을 사용해 본 바 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.01.17 - [.NET/C#] - [C# 12와 .NET 8] 3. 흐름제어, Type 변환, 예외 처리&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1706578057425&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 3. 흐름제어, Type 변환, 예외 처리&quot; data-og-description=&quot;이번에는 변수를 사용한 간단한 동작과 분기, pattern matching 수행, 구문 혹은 block의 반복과 여러 값을 저장하기 위한 array, 특정 type에서 다른 type으로의 변수나 표현식에 대한 변환, 예외 처리 그&quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4OL4B/hyVb4zeZXV/T6N0xWBDuaHldpiUtrBu30/img.png?width=800&amp;amp;height=50&amp;amp;face=0_0_800_50,https://scrap.kakaocdn.net/dn/cDUl13/hyVb1if7fL/bE4iwYWkbroLMpRB3s1kS1/img.png?width=800&amp;amp;height=50&amp;amp;face=0_0_800_50,https://scrap.kakaocdn.net/dn/A16Rk/hyVb7JxjQu/KHfxggfGBT4kC97Cf8g1Ek/img.png?width=979&amp;amp;height=220&amp;amp;face=0_0_979_220&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4OL4B/hyVb4zeZXV/T6N0xWBDuaHldpiUtrBu30/img.png?width=800&amp;amp;height=50&amp;amp;face=0_0_800_50,https://scrap.kakaocdn.net/dn/cDUl13/hyVb1if7fL/bE4iwYWkbroLMpRB3s1kS1/img.png?width=800&amp;amp;height=50&amp;amp;face=0_0_800_50,https://scrap.kakaocdn.net/dn/A16Rk/hyVb7JxjQu/KHfxggfGBT4kC97Cf8g1Ek/img.png?width=979&amp;amp;height=220&amp;amp;face=0_0_979_220');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 3. 흐름제어, Type 변환, 예외 처리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 변수를 사용한 간단한 동작과 분기, pattern matching 수행, 구문 혹은 block의 반복과 여러 값을 저장하기 위한 array, 특정 type에서 다른 type으로의 변수나 표현식에 대한 변환, 예외 처리 그&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이제 pattern matching에 대한 좀 더 상세한 내용을 더 알아보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 비행 승객 정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예제를 통해서는 비행기에 탑승하는 다양한 유형의 승객을 표현하기 위한 class를 정의할 것이며 그들에 대한 표값을 결정하기 위해 pattern matching과 함께 switch 문을 사용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NetLibraryStandard2 project에 FlightPatterns.cs이름의 file을 추가하고 다른 속성을 가진 3개의 승객유형을 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685583332510&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace NetLibraryStandard2
{
	public class Passenger
	{
		public string? Name { get; set; }
	}

	public class BusinessClassPassenger : Passenger
	{
		public override string ToString()
		{
			return $&quot;Business Class: {Name}&quot;;
		}
	}

	public class FirstClassPassenger : Passenger
	{
		public int AirMiles { get; set; }
		public override string ToString()
		{
			return $&quot;First Class with {AirMiles:N0} air miles: {Name}&quot;;
		}
	}

	public class CoachClassPassenger : Passenger
	{
		public double CarryOnKG { get; set; }
		public override string ToString()
		{
			return $&quot;Coach Class with {CarryOnKG:N2} KG carry on: {Name}&quot;;
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ToString method에 대한 재정의 부분은 추후에 자세히 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 여러 유형과 속성에 대한 다섯 명의 승객을 포함하는 개체 배열을 정의하고 이들을 열거하면서 해당 승객에 대한 비용을 출력하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685583638973&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Passenger[] passengers = {
	new FirstClassPassenger { AirMiles = 1_419, Name = &quot;Suyoung&quot; },
	new FirstClassPassenger { AirMiles = 16_562, Name = &quot;Lyujung&quot; },
	new BusinessClassPassenger { Name = &quot;Junghee&quot; },
	new CoachClassPassenger { CarryOnKG = 25.7, Name = &quot;Dongsu&quot; },
	new CoachClassPassenger { CarryOnKG = 0, Name = &quot;Ayoung&quot; },
};

foreach (Passenger passenger in passengers)
{
	decimal flightCost = passenger switch {
		FirstClassPassenger p when p.AirMiles &amp;gt; 35000 =&amp;gt; 1500M,
		FirstClassPassenger p when p.AirMiles &amp;gt; 15000 =&amp;gt; 1750M,
		FirstClassPassenger _ =&amp;gt; 2000M,
		BusinessClassPassenger _ =&amp;gt; 1000M,
		CoachClassPassenger p when p.CarryOnKG &amp;lt; 10.0 =&amp;gt; 500M,
		CoachClassPassenger _ =&amp;gt; 650M,
		_ =&amp;gt; 800M
	};

	Console.WriteLine($&quot;Flight costs {flightCost:C} for {passenger}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 아래 3가지 사항에 주목합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개체에 대한 속성에 pattern match를 구현하기 위해 표현식에서 사용될 수 있는 p와 같은 지역변수 이름을 사용해야 합니다.&lt;/li&gt;
&lt;li&gt;type에서만 pattern match를 구현하기 위해서는 지역변수를 사용하는 대신 _를 사용해 code를 단순화시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;_ (under bar)는 또한 switch 표현식에서 기본 분기를 나타내기 위해 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6i6id/btsDIBu7E2z/F6vcdvh3wjRJL1Xv8KqDK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6i6id/btsDIBu7E2z/F6vcdvh3wjRJL1Xv8KqDK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6i6id/btsDIBu7E2z/F6vcdvh3wjRJL1Xv8KqDK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6i6id%2FbtsDIBu7E2z%2FF6vcdvh3wjRJL1Xv8KqDK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;127&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) C# 9부터 적용된 향상된 pattern matching&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 C# 8에서 작동할 수 있는 code이며 C# 9부터는 좀 더 향상된 문법을 사용할 수 있습니다. type matching에서는 더 이상 _ (under bar) 문자를 사용할 필요가 없어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 위 예제의 C# 8 문법을 주석처리하고 C# 9부터 사용되는 문법을 적용하여 first-class승객에 대한 중첩된 switch 표현식을 사용하도록 변경하고 &amp;gt;와 같은 조건부에 대한 새로운 지원을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685584507379&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//FirstClassPassenger p when p.AirMiles &amp;gt; 35000 =&amp;gt; 1500M,
//FirstClassPassenger p when p.AirMiles &amp;gt; 15000 =&amp;gt; 1750M,
//FirstClassPassenger _ =&amp;gt; 2000M,
FirstClassPassenger p =&amp;gt; p.AirMiles switch
{
	&amp;gt; 35000 =&amp;gt; 1500M,
	&amp;gt; 15000 =&amp;gt; 1750M,
	_ =&amp;gt; 2000M
},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 동일한 결과를 확인할 수 있습니다. 해당 예제와 같은 상황에서는 또한 중첩된 switch 표현식을 사용하고 싶지 않다면 속성 pattern을 관계형 pattern과 결합하여 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685584696316&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FirstClassPassenger { AirMiles: &amp;gt; 35000 } =&amp;gt; 1500,
FirstClassPassenger { AirMiles: &amp;gt; 15000 } =&amp;gt; 1750M,
FirstClassPassenger =&amp;gt; 2000M,&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세한 사항은 아래 공식문서를 통해 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching&quot;&gt;Pattern matching overview - C# guide - C# | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;9. record 사용&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record라는 새로운 언어기능을 살펴보기 전에 C# 9부터 도입된 일부 관계된 기능을 먼저 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 초기화 전용(Init-only) 속성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 여러 예제를 통해 개체에 대한 초기화 구문으로 개체의 instance를 생성해 왔고 초기 속성을 설정했습니다. 그런데 이들 속성은 instance이후에도 값이 바뀔 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 속성이 읽기 전용(read-only)과 같은 속성처럼 다뤄져야 하는 경우도 있으며 따라서 instance를 생성할 때 설정되고 이후에는 바뀔 수 없어야 하는 경우 새로운 init keyword를 사용해 이러한 고민을 해결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NetLibraryStandard2 project에서 Records.cs이름의 새로운 file을 생성하고 2개의 불변(immutable) 속성을 가진 person class를 아래와 같이 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685602000221&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace NetLibraryStandard2
{
	public class ImmutablePerson
	{
		public string? FirstName { get; init; }
		public string? LastName { get; init; }
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 위 ImmutablePerson에 대한 instance를 생성하고 속성 중 하나의 속성에 대한 값의 변경을 시도합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685602232647&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ImmutablePerson hong = new()
{
	FirstName = &quot;hong&quot;,
	LastName = &quot;gildong&quot;
};

hong.FirstName = &quot;Geoff&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 다음과 같은 error를 표시하게 될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDZ5LV/btsDKPeQId7/TKYQAAhXBZ4NN8MYaMjHs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDZ5LV/btsDKPeQId7/TKYQAAhXBZ4NN8MYaMjHs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDZ5LV/btsDKPeQId7/TKYQAAhXBZ4NN8MYaMjHs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDZ5LV%2FbtsDKPeQId7%2FTKYQAAhXBZ4NN8MYaMjHs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1053&quot; height=&quot;114&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;FirstName을 개체 초기화에서 설정하지 않는다 하더라도 일단 초기화 이후에는 값을 설정할 수 없습니다. 만약 특정 속성에 대해 값이 값이 설정되도록 강제하려면 required keyword를 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Record의 이해&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화 전용 속성은 C#에 일부 불변성을 제공하며 이러한 개념은 record를 통해서도 가져갈 수 있습니다. record는 class keyword대신 record keyword를 사용해 정의되며 개체 전체를 불변으로 만들 수 있고 비교동작시 값처럼 동작합니다. class와 record 그리고 값 type에 대한 자세한 사항은 추후에 다뤄볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record는 instance이후에 바뀔 수 있는 어떠한 속성이나 field로 가질 수 없습니다. 대신 이미 존재하는 것으로부터 새로운 record를 생성할 수는 있는데 이를 통해 바뀐 값으로의 instance를 얻을 수 있습니다. 이러한 방식을&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;non-destructive mutation이라고 하는데 C# 9부터 with keyword를 통해 도입하여 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;Records.cs에서 ImmutableVehicle이름의 record를 아래와 같이 추가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685603162532&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public record ImmutableVehicle
{
	public int Wheels { get; init; }
	public string? Color { get; init; }
	public string? Brand { get; init; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 필요한 값을 통해 ImmutableVehicle에 대한 instance를 아래와 같이 생성할 수 있습니다. 해당 car instance는 이후 불변으로 속성은 값을 바꿀 수 없습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685603379619&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ImmutableVehicle car = new()
{
	Brand = &quot;Spark&quot;,
	Color = &quot;Black&quot;,
	Wheels = 4
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상술하였듯 with keyword를 사용해 기존 instance에 대한 값이 바뀐 복사본을 생성하고 각각의 속성값을 확인하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685603488010&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ImmutableVehicle repaintedCar = car with { Color = &quot;White&quot; };

Console.WriteLine($&quot;Original car color was {car.Color}.&quot;);
Console.WriteLine($&quot;New car color is {repaintedCar.Color}.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같은 결과를 표시할 것입니다. 복사본에서 car의 color가 변경되었음에 주목하시기 바랍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;89&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blgDlX/btsDHrNoaY7/XhxLXvMjAdLF3vKqnKabDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blgDlX/btsDHrNoaY7/XhxLXvMjAdLF3vKqnKabDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blgDlX/btsDHrNoaY7/XhxLXvMjAdLF3vKqnKabDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblgDlX%2FbtsDHrNoaY7%2FXhxLXvMjAdLF3vKqnKabDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;89&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;89&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Record 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record 사용으로 인해 확인가능한 가장 중요한 변화중 하나는 Record를 서로 비교하는 경우 확인할 수 있습니다. 같은 속성과 값을 가진 2개의 Record는 같은 것으로 취급됩니다. 별것 아닌 것 같이 들릴 수도 있겠지만 class를 사용하는 경우는 같은 값을 가지고 있다 하더라도 기본적으로 참조 memory주소값을 비교하기 때문에 다른 것으로 취급됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706573083965&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class AnimalClass
{
  public string Name { get; set; }
  public string Species { get; set; }
}

public record AnimalRecord
{
  public string Name { get; set; }
  public string Species { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제는 같은 형식으로 class와 record type을 각각 정의하고 있습니다. 이들에 대한 비교를 수행하기 위해 각각의 instance를 생성하고 아래와 같이 비교합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706573380133&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;AnimalClass ac1 = new() { Name = &quot;nyao&quot;, Species = &quot;Cat&quot; };
AnimalClass ac2 = new() { Name = &quot;nyao&quot;, Species = &quot;Cat&quot; };
Console.WriteLine($&quot;ac1 == ac2: {ac1 == ac2}&quot;);

AnimalRecord ar1 = new() { Name = &quot;Mong&quot;, Species = &quot;Dog&quot; };
AnimalRecord ar2 = new() { Name = &quot;Mong&quot;, Species = &quot;Dog&quot; };
Console.WriteLine($&quot;ar1 == ar2: {ar1 == ar2}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;137&quot; data-origin-height=&quot;32&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvTHHP/btsD6yxEuWp/naQNMNef1K6juuqUvZKO91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvTHHP/btsD6yxEuWp/naQNMNef1K6juuqUvZKO91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvTHHP/btsD6yxEuWp/naQNMNef1K6juuqUvZKO91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvTHHP%2FbtsD6yxEuWp%2FnaQNMNef1K6juuqUvZKO91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;137&quot; height=&quot;32&quot; data-origin-width=&quot;137&quot; data-origin-height=&quot;32&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째(class)는 같은 속성과 값을 가지고 있음에도 불구하고 같지 않다고 표시하고 있지만 두 번째(record)는 정확히 동일함으로 표시하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Record에서의 positional data member&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record를 정의하는 문법은 positional data member의 사용으로 훨씬 간소화될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Record에서 data member 간소화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중괄호를 통한 개체초기화문을 사용하는 대신 위에서 이미 해본 것처럼 positional 매개변수를 생성자에서 제공할 수 있고 또한 개체를 개별적으로 분해하기 위한 형식분해를 같이 결합할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685663120576&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public record ImmutableAnimal
{
	public string Name { get; init; }
	public string Species { get; init; }
	public ImmutableAnimal(string name, string species)
	{
		Name = name;
		Species = species;
	}
	public void Deconstruct(out string name, out string species)
	{
		name = Name;
		species = Species;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 위와 같은 속성과 생성자, 형식분해는 자동으로 생성될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record.cs에서 위 예제를 아래와 같이 positional record로 알려진 간소화문을 사용하여 record를 재작성해 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685663332080&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public record ImmutableAnimal(string Name, string Species);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다수의 line에 걸쳐 작성했던 record문을 위의 한 줄로 줄였습니다. Program.cs에서는 ImmutableAnimal에 대한 생성자와 형식분해문을 아래와 같이 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685663606586&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ImmutableAnimal oscar = new(&quot;Milan&quot;, &quot;Labrador&quot;);

var (who, what) = oscar;
Console.WriteLine($&quot;{who} is a {what}.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;79&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wvee1/btsDQf4TVWL/tYpchvHYsJ5vP32YBcxPv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wvee1/btsDQf4TVWL/tYpchvHYsJ5vP32YBcxPv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wvee1/btsDQf4TVWL/tYpchvHYsJ5vP32YBcxPv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwvee1%2FbtsDQf4TVWL%2FtYpchvHYsJ5vP32YBcxPv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;79&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;79&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 가변성과 record&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record를 생각할때 초기화이후에 instance 속성과 값형 field의 값이 바뀔 수 없는 불변성(immutable)부터 떠올리기 쉽지만 이는 사실 record가 어떻게 정의되었는가에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같이 Record가 사용되었을 경우&lt;/p&gt;
&lt;pre id=&quot;code_1707463160799&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public record class C1
{
    public string? Name { get; set; }
}

public record class C2(string? Name);

public record struct S1
{
    public string? Name { get; set; }
}

public record struct S2(string? Name);

public readonly record struct S3(string? Name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 각 type에 대한 instance를 생성하고 Name에 대한 초기값을 설정한 뒤 다시 Name에 대한 속성값을 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707463349330&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;C1 c1 = new() { Name = &quot;Hong&quot; };
c1.Name = &quot;Kim&quot;;
C2 c2 = new(Name: &quot;Hong&quot;);
c2.Name = &quot;Kim&quot;;

S1 s1 = new() { Name = &quot;Hong&quot; };
s1.Name = &quot;Kim&quot;;
S2 s2 = new(Name: &quot;Hong&quot;);
s2.Name = &quot;Kim&quot;;
S3 s3 = new(Name: &quot;Hong&quot;);
s3.Name = &quot;Kim&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런뒤 build를 시도하면 compiler error CS8852를 통해 이들 중 2개의 type이 초기화 이후에 불변성이 됨을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C1은 가변성이지만 C2는 불변이 되며 S1과 S2는 가변이지만 C3는 불변입니다. Microsoft는 record와 관련하여 몇가지 흥미로운 design을 적용하였습니다. 때문에 record, class, struct를 섞어서 사용할고 각각의 선언에서 다른 type을 사용할때&amp;nbsp; 이들 동작에 대한 차이를 기억하고 있어야 합니다.&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>C# 8</category>
      <category>class</category>
      <category>Field</category>
      <category>method</category>
      <category>OOP</category>
      <category>partial</category>
      <category>pattern matching</category>
      <category>Record</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/595</guid>
      <comments>https://cliel.tistory.com/entry/C-12-%EA%B3%BC-NET-8-5-OOP-Object-Oriented-Programming#entry595comment</comments>
      <pubDate>Tue, 30 Jan 2024 10:27:45 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 4. Debuging과 Testing</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-4-Debuging%EA%B3%BC-Testing</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;code의 재사용을 위한 함수의 작성과 개발동안의 logic error에 대한 debugging, runtime에서의 logging, code의 bug제거와 신뢰성 및 안정성을 높이기 위한 unit test 등은 개발과정에서 매우 중요한 요소로 취급되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 함수 작성&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;programming에서의 기본적인 원칙은 흔히 DRY불리는 '반복하지 마라'입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;programming동안에 같은 구문을 작성하고 또 그것을 반복하고 있다면 이들 구문을 함수로 전환할 필요가 있습니다. 함수는 application전체에서 하나의 작은 작업의 단위를 처리하는 부분으로서 예로 부가세 계산 logic과 같은 것들을 들 수 있으며 이러한 함수는 회계 application의 여러 곳에서 재사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;program에서 함수는 일반적으로 입력과 출력을 가지며 대게 한쪽 끝에서 원재료를 투입하고 다른 쪽에서 제조된 item이 나오는 black box로도 표현됩니다. 일단 생성된 것을 철저하게 debuging하고 test 한다면 향후에 해당 application을 유지관리하는데 상당한 도움이 될 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;함수는 OOP세계에서 class에 소속될 수 있는데 이때는 이를 Method라고 합니다. C#에서 함수는 Method와 같은 의미로 해석될 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) top-level program과 function&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6부터 console app의 기본 project template에서는 C# 9에서 도입된 top-level program을 사용하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 작성하면 자동적으로 생성된 Program class와 &amp;lt;Main&amp;gt;$ method에서 이들이 어떻게 작동하는지를 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 Program.cs에서 class를 import하고 해당 class의 method를 호출하고 있으며 또한 함수 하나를 정의하고 해당 함수를 호출하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683508719021&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.Console;

WriteLine(&quot;Hello, World!&quot;);

DoSomething();

void DoSomething()
{
	WriteLine(&quot;Doing something!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;compiler는 자동적으로 &amp;lt;Main&amp;gt;$ 함수와 함께 Program class를 생성하며 위 예제와 같이 입력된 구문과 함수를 &amp;lt;Main&amp;gt;$ method안으로 이동시킨 뒤 함수의 이름을 아래와 같이 바꾸게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683508930290&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.Console;
partial class Program
{
	static void &amp;lt;Main&amp;gt;$(String[] args)
	{
		WriteLine(&quot;Hello, World!&quot;);
		&amp;lt;&amp;lt;Main&amp;gt;$&amp;gt;g__DoSomething|0_0();
		
		void &amp;lt;&amp;lt;Main&amp;gt;$&amp;gt;g__DoSomething|0_0()
		{
			WriteLine(&quot;Doing something!&quot;);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 compiler가 어떤구문이 어디에 필요한지를 파악할 수 있도록 하기 위해서는 다음과 같이 규칙을 따라야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Import문(using)은 Program.cs file의 상단에 위치해야 합니다.&lt;/li&gt;
&lt;li&gt;&amp;lt;Main&amp;gt;$ function안으로 옮겨져야할 구문은 Program.cs file의 중간에 와야 합니다.&lt;/li&gt;
&lt;li&gt;함수는 Program.cs file의 하단에 위치해야 하며 이들은 지역 함수가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 지역함수의 경우 일부 제한을 가질 수 있으므로 위 마지막 규칙은 기억하고 있어야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 사용된 static이나 partial과 같은 keyword는 추후에 OOP를 다루는 과정에서 자세하게 살펴볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 정의하기 위한 보다 나은 방법은 분리된 file에서 함수를 정의하고 함수를 Program class에 정적(static) member로서 추가하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683510383270&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Function.cs와 같은 별도의 file에서 작성
partial class Program
{
	static void DoSomething()
	{
		Console.WriteLine(&quot;Doing something!&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683510419894&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Program.cs에서 작성
using static System.Console;
WriteLine(&quot;Hello, World!&quot;);
DoSomething();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;compiler가 Program class를 &amp;lt;Main&amp;gt;$ 함수와 함께 정의할 때 위 구문을 &amp;lt;Main&amp;gt;$ method안으로 옮기게 되고 Program class의 member로서 상기 함수를 병합하여 아래와 같은 결과물을 만들게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683510615902&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.Console;
partial class Program
{
	static void &amp;lt;Main&amp;gt;$(String[] args)
	{
		WriteLine(&quot;Hello, World!&quot;);
		DoSomething();
	}

	static void DoSomething() // define a function
	{
		WriteLine(&quot;Doing something!&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Program.cs에서 호출될 모든 함수는 별도의 분리된 file에서 생성하고 partial Program class안에서 이들을 호출하는 방법으로 사용하면 자동적으로 생성된 Program class의 &amp;lt;Main&amp;gt;$ method안에 지역함수로 들어가는 대신 &amp;lt;Main&amp;gt;$ method와 동일한 수준으로 병합될 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 구구단&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구구단을 공부하기 위해 구구단표를 만들어야 한다고 가정해 보면, 예를 들어 7단의 경우 아래와 같은 구구단을 생성할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;7 &amp;times; 1 = 7&lt;br /&gt;7 &amp;times; 2 = 14&lt;br /&gt;7 &amp;times; 3 = 21&lt;br /&gt;...&lt;br /&gt;7 &amp;times; 9 = 63&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구구단을 공부하는 수준에 따라서 10이나 12, 심지어 20까지의 곱셈이 들어간 표를 생성할 수도 있을 것입니다. 이전에 우리는 for문에 대해서 배웠고 일반적인 pattern에서 반복작업을 수행하는 데 사용할 수 있음을 알고 있습니다. 이를 통해 7단을 9 곱셈까지 표현하려면 다음과 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683511935351&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int row = 1; row &amp;lt;= 9; row++)
{
	Console.WriteLine($&quot;{row} x 7 = {row * 7}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위에서 처럼 항상 7단에 9곱셈까지 한정하여 표시하는 대신 이를 모든 숫자에 대한 원하는 만큼의 곱셈으로 표현하는 유연성을 가지기 위해서는 함수를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 구구단 함수 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 만들어볼 함수는 임의의 숫자에 대한 구구단을 표시하며 임의의 숫자까지(기본값 9)의 곱셈을 표시하도록 할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 위해 csStudy04 solution을 생성하고 WritingFunctions project를 추가합니다. 해당 project에서는 Function.cs file을 추가한 뒤 아래와 같이 partial Program class에 TimesTable이름의 함수를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683513046024&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	static void TimesTable(byte number, byte size = 9)
	{
		Console.WriteLine($&quot;This is the {number} times table with {size} rows:&quot;);

		for (int row = 1; row &amp;lt;= size; row++)
		{
			Console.WriteLine($&quot;{number} x {row} = {number * row}&quot;);
		}

		Console.WriteLine();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 예제는 다음의 특징을 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TimesTable은 number이름의 매개변수로 전달된 byte값을 가져야 합니다.&lt;/li&gt;
&lt;li&gt;TimesTable은 선택적으로 size라는 이름의 매개변수로 전달된 byte값을 가져야 합니다. 값이 전달되지 않으면 기본은 12가 됩니다.&lt;/li&gt;
&lt;li&gt;TimesTable은 static method인 &amp;lt;Main&amp;gt;$에서 호출되어야 하므로 static method입니다.&lt;/li&gt;
&lt;li&gt;TimesTable은 호출자에게 값을 반환하지 않으므로 함수의 이름앞에 void keyword를 사용하여 선언될 수 있습니다.&lt;/li&gt;
&lt;li&gt;TimesTable은 전달된 수의 구구단을 출력하기 위해 number와 size를 사용하는 for문을 사용합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 구문을 모두 삭제하고 number 매개변수의 byte값을 전달하여 위 함수를 호출하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683513701849&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TimesTable(7);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project를 실행하면 다음의 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZblKi/btsDxWsmCEV/61H7z56tKH9qtKpKTPsHR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZblKi/btsDxWsmCEV/61H7z56tKH9qtKpKTPsHR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZblKi/btsDxWsmCEV/61H7z56tKH9qtKpKTPsHR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZblKi%2FbtsDxWsmCEV%2F61H7z56tKH9qtKpKTPsHR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;226&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 7단에 20까지의 곱셈을 표시하도록 다음과 같이 함수를 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683513875953&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TimesTable(7, 20);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVzGi5/btsDAb3DrN7/Cr7J6R344ScEH9TNwEtcd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVzGi5/btsDAb3DrN7/Cr7J6R344ScEH9TNwEtcd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVzGi5/btsDAb3DrN7/Cr7J6R344ScEH9TNwEtcd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVzGi5%2FbtsDAb3DrN7%2FCr7J6R344ScEH9TNwEtcd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;377&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;함수가 하나 또는 그 이상의 매개변수를 가지고 있을때 값을 전달하는 것만으로 충분한 의미를 제공하지 못한다면 해당 값과 함께 선택적으로 매개변수의 이름을 다음과 같이 지정할 수 있습니다.&lt;br /&gt;TimesTable(number: 7, size: 20);&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 TimesTable함수를 호출할때는 0부터 255까지의 byte값을 전달해야 합니다. 매개변수가 byte형식이기 때문인데 따라서 int나, double, string과 같은 형식의 값을 전달하면 다음과 같이 error를 유발하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qHZLN/btsDAEErEm9/VSfWcuK4as5ZKZSTgw1Sk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qHZLN/btsDAEErEm9/VSfWcuK4as5ZKZSTgw1Sk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qHZLN/btsDAEErEm9/VSfWcuK4as5ZKZSTgw1Sk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqHZLN%2FbtsDAEErEm9%2FVSfWcuK4as5ZKZSTgw1Sk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;413&quot; height=&quot;122&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 인수와 매개변수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 개발자가 일상적으로 사용하는 단어로 인수와 매개변수라는 말이 있는데 개발자들 중에서도 이 2개의 용어를 혼동하는 경우가 많습니다. 엄밀히 말하면 이 2개의 용어는 전혀 다르지만 종종 같은 뜻으로 사용되는 경우가 있는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수(parameter)는 함수안에서 정의되는 변수입니다. 예를 들어 Hire 함수 안에서 startDate는 매개변수입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683532167465&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void Hire(DateTime startDate)
{
	// implementation
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method가 호출될때 method의 매개변수로 전달한 data를 인수(argument)라고 합니다. 예를 들어 Hire 함수로 전달한 when이 인수가 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683532420561&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DateTime when = new(year: 2022, month: 11, day: 8);
Hire(when);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인수를 전달할때 매개변수의 이름을 지정하고자 한다면 아래와 같이 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683532457097&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DateTime when = new(year: 2022, month: 11, day: 8);
Hire(startDate: when);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 Hire 함수를 호출한다고 했을 때 startDate는 매개변수, when은 인수가 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Microsoft 공식 문서에서도 'named and optional arguments'와 'named and optional parameters'를 같은 의미로 사용하고 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments&quot;&gt;Named and Optional Arguments - C# Programming Guide | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1683533065313&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Named and Optional Arguments - C# Programming Guide&quot; data-og-description=&quot;Named arguments in C# specify arguments by name, not position. Optional arguments can be omitted.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zi864/hySyg9slZ2/1gB9yeA6VCOzxqSCNa6PSK/img.png?width=64&amp;amp;height=64&amp;amp;face=0_0_64_64&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zi864/hySyg9slZ2/1gB9yeA6VCOzxqSCNa6PSK/img.png?width=64&amp;amp;height=64&amp;amp;face=0_0_64_64');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Named and Optional Arguments - C# Programming Guide&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Named arguments in C# specify arguments by name, not position. Optional arguments can be omitted.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 하나의 단일 개체가 context에 따라 매개변수와 인수 둘 다 될 수 있기 때문에 약간 난해할 수 있습니다. 예를 들어 Hire 함수 안에서 startDate 매개변수는 SaveToDatabase처럼 다른 함수로 인수로서 전달될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683533536458&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void Hire(DateTime startDate)
{
	//Something Do
	SaveToDatabase(startDate, employeeRecord);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;programming에서 가장 어려운것중 하나는 이름을 짓는 것입니다. 아래 예제는 C#에서 가장 중요한 Main 함수로서 'arguments'의 단축형인 args이름의 매개변수를 정의하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683533783971&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Main(String[] args)
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하자면 매개변수는 함수로의 입력을 정의하며 인수는 함수가 호출될때 함수로 전달되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 값을 반환하는 함수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 함수는 looping과 console로의 출력을 수행하는 함수이지만 어떠한 값도 반환하지 않습니다. 하지만 모든 함수를 그렇게 만들 수는 없는데 예를 들어 매출이나 부가세 계산이 필요하다고 했을 때 Europe에서 부가가치세(VAT) 율은 Switzerland의 8%에서 Hungary의 27% 범위가 될 수 있고 미국에서는 Oregon 0%에서 California의 8.25% 범위까지 될 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;세율은 언제나 바뀔 수 있고 많은 요인에 따라 달라집니다. 예제에서 사용된 값은 정확한 값이 아닙니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 세계의 다양한 지역에서 세율을 계산하는 함수로서 Functions.cs에서 CalculateTax라는 이름의 함수를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683598887340&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static decimal CalculateTax(decimal amount, string twoLetterRegionCode)
{
	decimal rate = 0.0M;

	switch (twoLetterRegionCode)
	{
		case &quot;CH&quot;: // Switzerland
			rate = 0.08M;
			break;
		case &quot;DK&quot;: // Denmark
		case &quot;NO&quot;: // Norway
			rate = 0.25M;
			break;
		case &quot;GB&quot;: // United Kingdom
		case &quot;FR&quot;: // France
			rate = 0.2M;
			break;
		case &quot;HU&quot;: // Hungary
			rate = 0.27M;
			break;
		case &quot;OR&quot;: // Oregon
		case &quot;AK&quot;: // Alaska
		case &quot;MT&quot;: // Montana
			rate = 0.0M;
			break;
		case &quot;ND&quot;: // North Dakota
		case &quot;WI&quot;: // Wisconsin
		case &quot;ME&quot;: // Maine
		case &quot;VA&quot;: // Virginia
			rate = 0.05M;
			break;
		case &quot;CA&quot;: // California
			rate = 0.0825M;
			break;
		default: // most US states
			rate = 0.06M;
			break;
	}

	return amount * rate;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 다음 사항에 주목해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CalculateTax는 2개의 입력을 받고 있으며 amount라는 이름의 매개변수는 지출금액이 되고 twoLetterRegionCode이름의 매개변수는 지출이 이루어진 지역이 됩니다.&lt;/li&gt;
&lt;li&gt;CalculateTax는 switch구문을 사용해 계산을 수행하며 10진수로서 지불해야할 sales tax나 VAT를 반환합니다. 따라서 함수이름 이전에 반환값의 data type으로 decimal형식의 값을 받을 수 있도록 해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 위에서 만든 CalculateTax함수를 호출하도록 하는데 이때 149와 같은 금액값과 FR 같은 정확한 지역 code를 전달해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683599432324&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;decimal taxToPay = CalculateTax(amount: 149, twoLetterRegionCode: &quot;FR&quot;);
Console.WriteLine($&quot;You must pay {taxToPay} in tax.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsWBcL/btsDAFDmDcf/oy1VK9DFIqZdSBbEh7JBv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsWBcL/btsDAFDmDcf/oy1VK9DFIqZdSBbEh7JBv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsWBcL/btsDAFDmDcf/oy1VK9DFIqZdSBbEh7JBv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsWBcL%2FbtsDAFDmDcf%2Foy1VK9DFIqZdSBbEh7JBv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;67&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 taxToPay는 '{taxToPay:C}'와 같은 방법으로 지역문화권을 통해 통화표시로 서식화할 수 있습니다. 예를 들어 대한민국의 경우 ￦28.9와 같이 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 숫자를 기수에서 서수로 변환하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셈을 위해 사용되는 숫자를 기수라고 하며 1, 2, 3등을 예로 들 수 있습니다. 반면 서수는 순서에 사용되며 첫번째(1st), 두 번째(2nd), 세 번째(3rd)와 같은 것들을 말합니다. 아래 예제는 기수를 서수로 변환하는 함수로서 Function.cs에 CardinalToOrdinal이름으로 정의하였습니다. 이 함수는 기수의 int값을 서수의 문자열값으로 변환하므로 1은 1st, 2는 2nd, 3은 3rd등으로 변환됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683611817415&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static string CardinalToOrdinal(int number)
{
	int lastTwoDigits = number % 100;

	switch (lastTwoDigits)
	{
		case 11:
		case 12:
		case 13:
			return $&quot;{number:N0}th&quot;;
		default:
			int lastDigit = number % 10;
			string suffix = lastDigit switch
			{
				1 =&amp;gt; &quot;st&quot;,
				2 =&amp;gt; &quot;nd&quot;,
				3 =&amp;gt; &quot;rd&quot;,
				_ =&amp;gt; &quot;th&quot;
			};
			return $&quot;{number:N0}{suffix}&quot;;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 함수에서는 아래 사항에 주목합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CardinalToOrdinal은 number라는 이름의 int type매개변수로 하나의 입력을 받으며 string type의 값을 반환합니다.&lt;/li&gt;
&lt;li&gt;switch문은 11, 12, 13의 특정 case를 처리하기 위해 사용되었습니다.&lt;/li&gt;
&lt;li&gt;switch표현식은 그외 다른 모든 case를 처리하는 데 사용되며 마지막 숫자가 1이면 st를, 2면 nd를, 3이면 rd접미사를 사용하고 그 외 다른 숫자라면 th접미사를 사용하도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Function.cs에서 RunCardinalToOrdinal이름의 함수를 작성합니다. 해당 함수는 1~150까지 loop를 순회하면서 각 숫자에 대해 CardinalToOrdinal함수를 호출하고 console에 반환된 문자열을 출력합니다. 이때 공백문자를 통해 각 출력을 구분하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683612421712&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void RunCardinalToOrdinal()
{
	for (int number = 1; number &amp;lt;= 150; number++)
	{
		Console.Write($&quot;{CardinalToOrdinal(number)} &quot;);
	}

	Console.WriteLine();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 구문을 모두 주석처리하고 RunCardinalToOrdinal method를 아래와 같이 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683612461247&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RunCardinalToOrdinal();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bV2P6y/btsDAQYYj9V/7g6Q3eoyNSJz4sfiVuJbi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bV2P6y/btsDAQYYj9V/7g6Q3eoyNSJz4sfiVuJbi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bV2P6y/btsDAQYYj9V/7g6Q3eoyNSJz4sfiVuJbi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbV2P6y%2FbtsDAQYYj9V%2F7g6Q3eoyNSJz4sfiVuJbi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;174&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 재귀호출을 통한 계승 계산&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계승은 시작되는 숫자에서 부터 1 작은 수를 곱하고 다시 그 숫자에 1 작은 수를 곱하여 시작수가 1이 될 때까지 계산합니다. 따라서 5의 계승은 5 &amp;Chi; 4 &amp;Chi; 3 &amp;Chi; 1이 되므로 120입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계승 함수는 음수가 아닌 아래와 같이 1, 2, 3, 4와 같은 정수를 위해서만 정의됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;0! = 1&lt;br /&gt;n! = n &amp;times; (n &amp;minus; 1)!, for n &amp;isin; { 1, 2, 3, ... }&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계승은 '5!'와 같이 쓰며 여기서 느낌표를 계승이라고 표현하므로 '5! = 120은 5의 계승은 120이다.'라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예제는 Factorial함수로서 매개변수로 전달된 int값에 대한 계승을 계산할 것입니다. 여기서는 '재귀'라고 하는 방법을 사용했는데 이는 직접적이든 간접적이든 구현된 범위 안에서 자신을 호출하는 함수를 의미합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683618407453&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int Factorial(int number)
{
	if (number &amp;lt; 0)
		throw new ArgumentException(message: &quot;factorial 함수는 정수만 사용할 수 있습니다. { number }&quot;, paramName: nameof(number));
	else if (number == 0)
		return 1;
	else
		return number * Factorial(number - 1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전과 마찬가지로 위의 예제에서도 몇몇 주목할만한 부분이 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매개변수 number가 음수가 된다면 Factorial은 예외를 발생시킵니다.&lt;/li&gt;
&lt;li&gt;매개변수 number가 0이라면 1을 반환합니다.&lt;/li&gt;
&lt;li&gt;매개변수 number가 그외 다른 case가 되는 1보다 크다면 Factorial은 number보다 1적은 수를 전달하여 스스로를 호출함으로써 number에 대한 곱셈을 수행합니다.(재귀함수)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;재귀는 어떤면에서 현명한 방법이 될 수 있지만 잘못하면 너무 많은 함수를 호출하면 함수가 호출될 때마다 memory를 사용하게 되어 결국에는 overflow를 일으킬 수 있는 등의 문제를 발생시킬 수 있습니다. 반복은 간결함이 떨어질 수 있지만 C#과 같은 언어에서 더 실용적인 해결책이 될 수 있습니다. 이와 관련하여 아래 link를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursion_versus_iteration&quot;&gt;Recursion (computer science) - Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1683619151196&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Recursion (computer science) - Wikipedia&quot; data-og-description=&quot;From Wikipedia, the free encyclopedia Use of functions that call themselves Tree created using the Logo programming language and relying heavily on recursion. Each branch can be seen as a smaller version of a tree. In computer science, recursion is a metho&quot; data-og-host=&quot;en.wikipedia.org&quot; data-og-source-url=&quot;https://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursion_versus_iteration&quot; data-og-url=&quot;https://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursion_versus_iteration&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bs83Fb/hySytvgHyz/5OUligHtLLosOX8QlcylYk/img.jpg?width=380&amp;amp;height=501&amp;amp;face=0_0_380_501,https://scrap.kakaocdn.net/dn/6zAIS/hySAaniibD/LjLggaTZPCiOkhnb237bK0/img.jpg?width=380&amp;amp;height=501&amp;amp;face=0_0_380_501,https://scrap.kakaocdn.net/dn/beCvui/hySyrxrSHA/kxDyAsWzAOKAvvefrOJ60k/img.jpg?width=300&amp;amp;height=396&amp;amp;face=0_0_300_396&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursion_versus_iteration&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursion_versus_iteration&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bs83Fb/hySytvgHyz/5OUligHtLLosOX8QlcylYk/img.jpg?width=380&amp;amp;height=501&amp;amp;face=0_0_380_501,https://scrap.kakaocdn.net/dn/6zAIS/hySAaniibD/LjLggaTZPCiOkhnb237bK0/img.jpg?width=380&amp;amp;height=501&amp;amp;face=0_0_380_501,https://scrap.kakaocdn.net/dn/beCvui/hySyrxrSHA/kxDyAsWzAOKAvvefrOJ60k/img.jpg?width=300&amp;amp;height=396&amp;amp;face=0_0_300_396');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Recursion (computer science) - Wikipedia&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;From Wikipedia, the free encyclopedia Use of functions that call themselves Tree created using the Logo programming language and relying heavily on recursion. Each branch can be seen as a smaller version of a tree. In computer science, recursion is a metho&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;en.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서 아래와 같이 RunFactorial이름의 함수를 추가합니다. 이 함수는 1부터 15까지의 숫자에 대한 factorial을 표시하기 위해 for문을 사용하며 N0 code를 사용해 소수점 0자리와 함께 , 로 천 단위 숫자를 분리하는 서식화를 적용하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683619377637&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void RunFactorial()
{
	for (int i = 1; i &amp;lt;= 15; i++)
	{
		Console.WriteLine($&quot;{i}! = {Factorial(i):N0}&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RunCardinalToOrdinal method의 호출부분을 주석처리하고 RunFactorial method를 호출하도록 한 뒤 예제를 실행하면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FDvwR/btsDzwfQDn1/KulN8Kqe6XxjsUrohcawxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FDvwR/btsDzwfQDn1/KulN8Kqe6XxjsUrohcawxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FDvwR/btsDzwfQDn1/KulN8Kqe6XxjsUrohcawxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFDvwR%2FbtsDzwfQDn1%2FKulN8Kqe6XxjsUrohcawxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;287&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;287&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 통해서는 분명해 보이지 않을 수 있지만 13이상의 계승은 너무 큰 숫자로 int type을 overflow 합니다. 12! 는 479,001,600인데 이는 5억 가까이 됩니다. int변수로 저장가능한 최대 양수값은 20만입니다. 13! 은 6,227,020,800이므로 60억이며 이는 32bit integer로 저장될 때 어떠한 오류도 표시하지 않고 overflow로 처리됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 overflow가 발생하지 않도록 13!이상에서 32bit int대신 64bit long을 사용할 수 있지만 그런 조치를 함에도 불구하고 빠르게 overflow의 한계에 다시 도달하게 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 핵심은 숫자가 overflow될 수 있다는 것을 이해하고 직접 확인하는 것입니다. 12 이상의 factorial을 어떻게 계산할지를 공부하고자 하는 것이 아닙니다. 아래와 같이 Factorial함수를 수정하여 overflow를 확인할 수 있도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683620404494&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int Factorial(int number)
{
	if (number &amp;lt; 0)
		throw new ArgumentException(message: $&quot;factorial 함수는 정수만 사용할 수 있습니다. { number }&quot;, paramName: nameof(number));
	else if (number == 0)
		return 1;
	else
	{
		checked // overflow check
		{
			return number * Factorial(number - 1);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RunFactorial 함수도 for에서 시작되는 수를 -2로 변경하여 overflow와 Factorial method를 호출할 때의 예외를 처리될 수 있도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683620585471&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void RunFactorial()
{
	for (int i = -2; i &amp;lt;= 15; i++)
	{
		try
		{
			Console.WriteLine($&quot;{i}! = {Factorial(i):N0}&quot;);
		}
		catch (OverflowException)
		{
			Console.WriteLine($&quot;{i}! 32bit integer에서는 처리할 수 없는 수입니다.&quot;);
		}
		catch (Exception ex)
		{
			Console.WriteLine($&quot;{i}! throws {ex.GetType()}: {ex.Message}&quot;);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 결과는 다음과 같을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biKsvo/btsDyQk9RZg/OkRUDahqS1KwWV4IW1xXn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biKsvo/btsDyQk9RZg/OkRUDahqS1KwWV4IW1xXn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biKsvo/btsDyQk9RZg/OkRUDahqS1KwWV4IW1xXn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiKsvo%2FbtsDyQk9RZg%2FOkRUDahqS1KwWV4IW1xXn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;351&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) XML주석을 통해 함수 설명하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CardinalToOrdinal과 같은 함수를 호출할때는 보통 code editor는 기본적인 정보를 표시하는 tooltip을 보여주게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;62&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWlFs9/btsDxApw6Qv/zytzyUqjCiL8WoWk7g74DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWlFs9/btsDxApw6Qv/zytzyUqjCiL8WoWk7g74DK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWlFs9/btsDxApw6Qv/zytzyUqjCiL8WoWk7g74DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWlFs9%2FbtsDxApw6Qv%2FzytzyUqjCiL8WoWk7g74DK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;62&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;62&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상황에서 사용자에게 보여질 정보는 XML주석을 통해 추가될 수 있습니다. CardinalToOrdinal함수 위에서 slashe문자 3개를 연속으로 입력합니다. 그러면 해당 함수가 number라는 하나의 매개변수를 갖는다고 인식하고 거기에 맞는 XML주석의 틀이 아래와 같이 표시될 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683675894705&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// 
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;number&quot;&amp;gt;&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 주석틀 안에 CardinalToOrdinal함수에 관한 적절한 정보를 입력하면 되는데 summary는 함수에 대한 대략적인 설명을, param에는 해당 매개변수에 대한 설명을, returns에는 반환값에 대한 설명을 입력하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683676087014&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// 32-bit integer값을 전달하면 이에 맞는 서수로 변환하여 그 결과를 반환합니다.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;number&quot;&amp;gt;1, 2, 3과 같은 정수값을 입력합니다.&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;1st, 2nd, 3rd와 같은 서수로 변환된 값입니다.&amp;lt;/returns&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설명을 작성한 후 다시 함수의 호출 부분에서 mouse를 대면 다음과 같이 입력한 정보를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U4n6D/btsDxf0gr2q/wR47jHgj85TEd5lfTaYgYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U4n6D/btsDxf0gr2q/wR47jHgj85TEd5lfTaYgYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U4n6D/btsDxf0gr2q/wR47jHgj85TEd5lfTaYgYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU4n6D%2FbtsDxf0gr2q%2FwR47jHgj85TEd5lfTaYgYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;120&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능은 Sandcastle처럼 주석을 문서로 변환하는 도구와 함께 사용되도록 설계된 것으로 아래 link를 통해 Sandcasle에 대한 좀 더 자세한 정보를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/EWSoftware/SHFB&quot;&gt;GitHub - EWSoftware/SHFB: Sandcastle Help File Builder (SHFB).&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1683676520320&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - EWSoftware/SHFB: Sandcastle Help File Builder (SHFB).  A standalone GUI, Visual Studio integration package, and MSBuild&quot; data-og-description=&quot;Sandcastle Help File Builder (SHFB). A standalone GUI, Visual Studio integration package, and MSBuild tasks providing full configuration and extensibility for building help files with the Sandcast...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/EWSoftware/SHFB&quot; data-og-url=&quot;https://github.com/EWSoftware/SHFB&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fbLPZ/hySAgOSudv/O14QAAWnzDAvecNg189kM0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/EWSoftware/SHFB&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/EWSoftware/SHFB&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fbLPZ/hySAgOSudv/O14QAAWnzDAvecNg189kM0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - EWSoftware/SHFB: Sandcastle Help File Builder (SHFB). A standalone GUI, Visual Studio integration package, and MSBuild&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Sandcastle Help File Builder (SHFB). A standalone GUI, Visual Studio integration package, and MSBuild tasks providing full configuration and extensibility for building help files with the Sandcast...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;code를 입력하거나 함수이름에 mouse를 올려둘때 나타나는 tooltip은 보조적 기능에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역함수의 경우 이들이 선언된 외부에서는 사용될 수 없기 때문에 XML 주석을 지원하지 않습니다. 따라서 이들에 대한 문서화를 생성하는 것은 의미가 없으며 Visual Studio 2022나 Visual Studio Code도 인식하지 못하므로 tooltip조차 사용되지 않습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;지역 함수를 제외하고 code상의 모든 함수에 XML주석을 추가하기를 권장합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 함수에서 lambda사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;F#은 Microsoft의 강력한 Type의 함수형 programming언어로서 C#과 같이 .NET하에서 실행되기 위해 IL로 compile 됩니다.&amp;nbsp; 이러한 함수형 언어는 계산 system이 함수에 기반한 lambda계산에서 진화한 것인데 code는 일반적인 절차형 언어의 그것보다는 수학적 함수에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 언어에서 주목해야할 중요한 특징으로는 다음과 같은 것들이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단위화(Modularty) : C#에서 함수를 정의할 때와 동일한 이점이 함수형 언어에도 적용됩니다. 이를 통해 크고 복잡한 code는 더 작은 조각으로 나누어질 수 있습니다.&lt;/li&gt;
&lt;li&gt;불변성(Immutability) : 함수안에서 모든 변수는 바뀔 수 없습니다. 대신 기존에 존재하는 것으로부터 새로운 data값이 생성될 수 있습니다. 이러한 방식은 bug를 줄이는데도 도움이 됩니다.&lt;/li&gt;
&lt;li&gt;관리성(Maintainability) : Code는 깔끔하고 명확합니다.(수학적 성향을 가진 개발자들을 위해!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 6 이후 Microsoft는 더욱 함수적인 접근법을 지원하기 위해 언어에 기능추가를 시도해 왔습니다. 예를 들어 C# 7에서의 tuple과 pattern matching, C# 8에서의 non-null 참조 type, 그리고 C# 9에서 pattern matching의 향상과 record 즉, 불변개체등을 들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 6에서 Microsoft는 식본문함수member(expression-bodied function member)의 지원을 추가했으며 이전의 예제에서도 이를 활용한 바 있습니다. C#에서 lambda는 함수로부터 반환값을 표현하기 위해 =&amp;gt; 문자를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자의 Fibonacci 수열은 항상 0과 1로 시작합니다. 그러면 수열의 나머지는 이전 2개의 숫자를 같이 더하는 규칙을 통해&amp;nbsp; 아래와 같이 생성됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%; text-align: center;&quot;&gt;0 1 1 2 3 5 8 13 21 34 55...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 수열의 다음 순서는 34 + 55이므로 89가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 Fibonacci수열로 명령적 함수와 선언적 함수를 구현하는것의 차이를 설명할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Function.cs에서 명령적 함수 형태로 FibImperative이름의 함수를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683707358025&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int FibImperative(int term)
{
	if (term == 1)
	{
		return 0;
	}
	else if (term == 2)
	{
		return 1;
	}
	else
	{
		return FibImperative(term - 1) + FibImperative(term - 2);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 RunFibImperative이름의 함수를 추가합니다. 이 함수는 1부터 30까지 순회하는 for문안에서 FibImperative 함수를 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683709399939&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void RunFibImperative()
{
	for (int i = 1; i &amp;lt;= 30; i++)
	{
		Console.WriteLine(&quot;The {0} term of the Fibonacci sequence is {1:N0}.&quot;, arg0: CardinalToOrdinal(i), arg1: FibImperative(term: i));
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Progam.cs에서는 기존의 구문을 모두 제거하고 RunFibImperative만을 호출하도록 합니다. 그리고 project를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qaQv7/btsDAbo13yO/Q1T7bEG6hjzjlGgMKxzuE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qaQv7/btsDAbo13yO/Q1T7bEG6hjzjlGgMKxzuE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qaQv7/btsDAbo13yO/Q1T7bEG6hjzjlGgMKxzuE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqaQv7%2FbtsDAbo13yO%2FQ1T7bEG6hjzjlGgMKxzuE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;455&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Function.cs로 돌아와 FibFunctional 함수를 추가합니다. 해당 함수는 선언적 형태로 작성되었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683709563195&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int FibFunctional(int term) =&amp;gt; term switch
{
	1 =&amp;gt; 0,
	2 =&amp;gt; 1,
	_ =&amp;gt; FibFunctional(term - 1) + FibFunctional(term - 2)
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 1부터 30까지 순회하는 for문 안에서 위의 함수를 호출하는 RunFibFunctional 함수를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683709677979&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void RunFibFunctional()
{
	for (int i = 1; i &amp;lt;= 30; i++)
	{
		Console.WriteLine(&quot;The {0} term of the Fibonacci sequence is {1:N0}.&quot;, arg0: CardinalToOrdinal(i), arg1: FibFunctional(term: i));
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 역시 Progam.cs안에서 RunFibImperative 함수를 호출하도록 하고 예제를 실행하면 이전과 동일한 결과가 됨을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. Debugging&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발과정에서는 문제점을 해결하기 위해 debugging이라는 과정을 거쳐야 하며 실제 그렇게 하기 위한 방법에 대해 알아보고자 합니다. 이를 위해 개발자는 Visual Studio 2022나 Visual Studio Code와 같은 debugging 도구를 가진 code editor를 사용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;어떤 경우는 Visual Studio Code에서 OmniSharp debugger를 설정하는 것이 어렵다고 느껴질 수 있는데 이에 대한 정보는 아래 link를 참고할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger.md&quot;&gt;omnisharp-vscode/debugger.md at master &amp;middot; OmniSharp/omnisharp-vscode &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1683713703053&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - OmniSharp/omnisharp-vscode: Official C# support for Visual Studio Code (powered by OmniSharp)&quot; data-og-description=&quot;Official C# support for Visual Studio Code (powered by OmniSharp) - GitHub - OmniSharp/omnisharp-vscode: Official C# support for Visual Studio Code (powered by OmniSharp)&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger.md&quot; data-og-url=&quot;https://github.com/OmniSharp/omnisharp-vscode&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eU10m/hySAcze5Zp/AijCXdbAlnscIACA7nb0R1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eU10m/hySAcze5Zp/AijCXdbAlnscIACA7nb0R1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - OmniSharp/omnisharp-vscode: Official C# support for Visual Studio Code (powered by OmniSharp)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Official C# support for Visual Studio Code (powered by OmniSharp) - GitHub - OmniSharp/omnisharp-vscode: Official C# support for Visual Studio Code (powered by OmniSharp)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) debugging에서 Visual Studio Code에 통합된 Terminal 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 OmmiSharp은 debugging동안에 내부 console을 사용하도록 console을 설정하는데 여기서 한가지 문제점은 ReadLine method를 통한 text입력과 같은 상호작용을 지원하지 않는다는 것입니다. 따라서 console대신 통합 termianl을 대신 사용할 수 있도록 설정을 변경할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;breakpoint를 걸고 단계적으로 code를 진행함으로서 debugging 하고자 하는 project의 folder로 들어간 뒤 .vscode folder로 들어가면 launch.json file을 볼 수 있습니다. 해당 file을 열어 console의 설정을 'internalConsole'에서 'integratedTerminal'로 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683782100705&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;version&quot;: &quot;0.3.2&quot;,
    &quot;configurations&quot;: [
    {
        &quot;console&quot;: &quot;integratedTerminal&quot;,
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 고의적인 bug로 code 생성하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제로서 실제 code editor의 debugger도구를 사용해 bug를 찾고 수정해볼 텐데 이를 위해 해당 bug 가진 console app을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy04 solution에서 'Debugging'이라는 이름의 project를 생성한 뒤 Program.cs를 아래와 같이 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683782577983&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double Add(double a, double b)
{
	return a * b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 해당 함수의 위에서 아래와 같이 몇개의 변수를 선언하고 값을 설정하는 구문과 함께 위 함수를 호출하는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683782837815&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double a = 4.5;
double b = 2.5;
double answer = Add(a, b);

Console.WriteLine($&quot;{a} + {b} = {answer}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;63&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhmYXS/btsDBO0UDi2/OhksaVNktI1kqMPH5Sjeu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhmYXS/btsDBO0UDi2/OhksaVNktI1kqMPH5Sjeu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhmYXS/btsDBO0UDi2/OhksaVNktI1kqMPH5Sjeu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdhmYXS%2FbtsDBO0UDi2%2FOhksaVNktI1kqMPH5Sjeu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;63&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;63&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 4.5와 2.5를 더한 7이 나와야 하지만 11.25가 나옴으로써 code안에 bug가 있음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 중단점설정및 debugging&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중단점(Breakpoint)은 Program의 상태를 확인하고 bug를 찾기 위해 실행 중 멈추고자 하는 code의 line을 표시하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022의 code editor에서 a이름의 변수를 선언한 구문이 있는 첫 행을 click 합니다. 그리고 Debug-&amp;gt;Toggle Breakpoint menu 또는 F9 key를 누릅니다. 그러면 다음과 같이 붉은색 원이 좌변 여백공간에 표시되고 해당 구문이 Breakpoint가 설정되었음을 알리기 위해 붉은색으로 강조될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nmoYL/btsDBsXRPuZ/Z3StHAId2YdiKo8FY2HSm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nmoYL/btsDBsXRPuZ/Z3StHAId2YdiKo8FY2HSm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nmoYL/btsDBsXRPuZ/Z3StHAId2YdiKo8FY2HSm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnmoYL%2FbtsDBsXRPuZ%2FZ3StHAId2YdiKo8FY2HSm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설정된 Breakpoint는 같은 동작을 통해 설정이나 해제를 반복할 수 있습니다. 또한 여백자체를 mouse 왼쪽 click해도 마찬가지 동작을 수행합니다. 여백을 오른쪽 click 하면 삭제, 비활성 혹은 조건변경이나 현재 breakpoint에 대한 동작을 의미하는 더 많은 option을 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Debug-&amp;gt;Start Debugging이나 F5 key를 누르면 Visual Studio는 console application을 시작하게 되고 실행시점이 설정해둔 breakpoint에 도달하게 되면 일시적으로 실행을 멈추게 됩니다. 이 상태를 break mode라고 하며 Visual Studio안에서 Locals(지역변수의 현재 값을 표시하는 창), Watch 1(정의한 모든 watch 표현식을 표시), Call Stack, Exception Settings 그리고 Immediate창을 확장하여 표시합니다. 또한 다음으로 실행될 line은 노란색으로 표시되며 여백으로부터 노란색 화살표가 해당 line을 다음과 같이 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lGqR4/btsDxnD09D4/tuQIrZwM2eDaKQKBn9Back/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lGqR4/btsDxnD09D4/tuQIrZwM2eDaKQKBn9Back/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lGqR4/btsDxnD09D4/tuQIrZwM2eDaKQKBn9Back/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlGqR4%2FbtsDxnD09D4%2FtuQIrZwM2eDaKQKBn9Back%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Debugging toolbar&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022는 Standard toolbar와 Debug toolbar라는 2개의 group으로 debug관련 button들을 가지고 있습니다. Standard toolbar는 debugging을 시작하거나 계속진행하고 동작중인 code의 변경사항을 hot reload하기 위한 것이며 그 외 tool의 나머지는 Debug toolbar로 분리되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOji0x/btsDxiCFRtl/Egcp6UieY2xGzGJA8XiICk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOji0x/btsDxiCFRtl/Egcp6UieY2xGzGJA8XiICk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOji0x/btsDxiCFRtl/Egcp6UieY2xGzGJA8XiICk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOji0x%2FbtsDxiCFRtl%2FEgcp6UieY2xGzGJA8XiICk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;615&quot; height=&quot;112&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Start/Continue/F5 : 이 button은 상황에 따라 다르게 동작합니다. project의 동작을 시작하거나 현재 위치에서 code의 종료시 또는 다른 breakpoint까지 실행을 계속 진행하도록 합니다.&lt;/li&gt;
&lt;li&gt;Hot Reload : 이 button은 compile된 code의 변경사항을 app을 재실행하지 않고도 다시 load 하도록 합니다.&lt;/li&gt;
&lt;li&gt;Break All : 이 button은 동작중인 app에서 다음 가능한 code의 line으로 일시중지합니다.&lt;/li&gt;
&lt;li&gt;Stop Debugging/Stop/Shift + F5 (붉은색 사각형) : 이 button은 debugging session을 중지합니다.&lt;/li&gt;
&lt;li&gt;Restart/Ctrl or Cmd + Shift + F5 (원형 화살표) : 이 button은 현재 debugging session을 중지한 뒤 곧장 program에 debgger를 연결시킨 뒤 다시 실행합니다.&lt;/li&gt;
&lt;li&gt;Show Next Statement : 이 button은 현재 cursor를 다음 실행될 구문으로 이동시킵니다.&lt;/li&gt;
&lt;li&gt;Step Info/F11, Step Over/F10, Step Out/Shift + F11 (반점이 있는 파란색 화살표) : 이들 button은 code구문을 다양한 방법을 통해 단계적으로 실행시킴으로써 실행 순간을 확인할 수 있도록 합니다.&lt;/li&gt;
&lt;li&gt;Show Threads in Source : 이 button은 debugging중인 application에 대한 thread를 검사하고 작업할 수 있도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Debugging Windows&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;debugging하는 동안 Visual Studio 2022에서는 여러 window화면을 통해 code를 단계적으로 실행하면서 변숫값의 변화등을 관찰하는 등 여러 유용한 정보를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;debugging시에 가장 많이 사용되는 windows창으로는 아래와 같은 것들이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VARIABLES : Locals를 포함해 모든 지역변수에 대한 이름, 값, type등을 자동적으로 확인할 수 있습니다. 이를 통해 code를 단계적으로 실행하는 동안 그때그때 변화되는 정보를 관찰합니다.&lt;/li&gt;
&lt;li&gt;WATCH / Watch 1 : 변수의 값과 임의로 입력한 표현식을 나타냅니다.&lt;/li&gt;
&lt;li&gt;CALL STACK : 함수 호출간 stack정보를 표시합니다.&lt;/li&gt;
&lt;li&gt;BREAKPOINTS : 모든 breakpoint를 표시하고 이들을 세밀하게 제어할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;break mode인 경우에는 edit의 아래에서 다음 window화면을 사용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DEBUG CONSOLE 또는 Immediate Window : code와의 상호작용을 통해 program의 상태를 질의할 수 있습니다. 예를 들어 변수의 이름을 입력해 해당 변수의 값을 확인하거나 1 + 2와 같은 식을 요청하여 그 결과를 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) code의 단계적 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio를 통해 code를 단계적으로 실행하려면 Debug -&amp;gt; Step Info menu를 선택하거나 toolbar에 있는 Step Into button 또는 F11키를 눌러줍니다. 그러면 노란색 강조 부분이 다음 line으로 이동하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Debug -&amp;gt; Step Over menu를 선택하거나 toolbar에 있는 Step Over button 또는 F10키를 눌러도 노란색 강조부분이 다음 line으로 이동해서 이전과 동일하다고 생각될 수 있습니다. 그러나 현재 단계적으로 실행되고 있는 부분이 method라면 Step Into는 debugge가 method실행 내부로 들어가게 되므로 method의 모든 line을 단계적으로 실행하게 되지만 Step Over는 method전체를 한 번에 실행한 후 method다음의 line으로 이동한다는 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Step Into를 눌러 method안으로 진입합니다. 이때 mouse pointer를 매개변수 a나 b위에 올려두면 tooltip을 통해 해당 변수의 현재값을 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 a * b식을 선택한 뒤 mouse 오른쪽 button을 눌러 Add Watch를 선택합니다. 해당 식은 Watch 1창으로 추가되고 a와 b의 *연산에 대한 결과가 11.25 임을 표시하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Watch 1 window에서 해당 식에 다시 mouse 오른쪽 button을 눌러 Delete Watch를 선택하여 추가된 a * b식을 삭제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 결과를 확인하여 문제가 무엇인지를 확인했으니 함수에서 *기호를 +로 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;debugging을 멈추고 다시 재 compile을 수행한 다음 toolbar에서 Restart button이나 Ctrl + Shift + F5 key를 눌러 debugging을 재시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 함수를 단계적으로 실행하고 계산되는 정확성을 확인한 후 Continue button을 누르거나 F5 key를 눌러줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HQVS9/btsDuSjXD6A/SfU6hvRawgZx3vRKVD7ON1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HQVS9/btsDuSjXD6A/SfU6hvRawgZx3vRKVD7ON1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HQVS9/btsDuSjXD6A/SfU6hvRawgZx3vRKVD7ON1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHQVS9%2FbtsDuSjXD6A%2FSfU6hvRawgZx3vRKVD7ON1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;187&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) breakpoint 활용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;breakpoint자체도 여러 유용한 설정을 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 debugging중이라면 toolbar의 Stop button을 누르거나 Debug -&amp;gt; Stop Debugging menu 또는 Shift + F5 key를 눌러 debugging을 중지합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Debug-&amp;gt;Delete All Breakpoints menu를 선택해 현재 설정된 모든 breakpoint를 삭제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;code상에 Console.WriteLine구문을 click 하고 F9 key 또는 Debug -&amp;gt; Toggle Breakpoint menu를 선택해 breakpoint를 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 breakpoint를 mouse오른쪽 button으로 click한 후 Conditions menu를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 설정화면에서 다음과 같이 answer변수가 9이상이 되어야 한다는 수식을 입력하고 'Close' button을 눌러줍니다. 이때 Is true는 입력한 수식으로 breakpoint가 작동하기 위해서는 true가 되어야 한다는 것을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5HgP4/btsDBQRV3bG/CkJi49YnQ2iiClcOdtjFB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5HgP4/btsDBQRV3bG/CkJi49YnQ2iiClcOdtjFB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5HgP4/btsDBQRV3bG/CkJi49YnQ2iiClcOdtjFB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5HgP4%2FbtsDBQRV3bG%2FCkJi49YnQ2iiClcOdtjFB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;289&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설정한 후 다시 debugging을 시도하면 breakpoint가 작동하지 않음을 알 수 있습니다. condition으로 입력한 수식의 조건이 맞지 않기 때문입니다. 따라서 같은 방법으로 condition을 6으로 설정한 뒤 다시 debugging을 시도하면 이번에는 breakpoint가 작동하게 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 조금 다르게 condition을 설정해볼텐데 condition설정화면에서 'Add condition'을 click 하여 새로운 condition을 입력할 수 있도록 합니다. 그리고 Hit count의 값을 다음과 같이 3으로 설정합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpqnRy/btsDBweS9hh/INUKIHhE6Dz0QiVXKR7aa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpqnRy/btsDBweS9hh/INUKIHhE6Dz0QiVXKR7aa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpqnRy/btsDBweS9hh/INUKIHhE6Dz0QiVXKR7aa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpqnRy%2FbtsDBweS9hh%2FINUKIHhE6Dz0QiVXKR7aa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;759&quot; height=&quot;309&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 설정은 breakpoint가 작동하기 위해서 중단점 부분의 구문이 3번 평가되어야 함을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정이 완료된 후 breakpoint로 mouse를 올리게 되면 다음과 같이 설정상태가 tooltip으로 표시되는데 이를 통해 condition설정화면을 다시 열지 않고도 현재의 설정상태를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;395&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9v2nF/btsDBv78WFJ/ElpeWIZELx77Yo9qs0fDlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9v2nF/btsDBv78WFJ/ElpeWIZELx77Yo9qs0fDlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9v2nF/btsDBv78WFJ/ElpeWIZELx77Yo9qs0fDlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9v2nF%2FbtsDBv78WFJ%2FElpeWIZELx77Yo9qs0fDlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;395&quot; height=&quot;128&quot; data-origin-width=&quot;395&quot; data-origin-height=&quot;128&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Hot reload&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hot reload는 app을 debugging하는 중에도 code를 변경하고 그 즉시 변경된 사항을 반영하여 app의 실행을 확인해 볼 수 있도록 하는 기능입니다. app이 가진 bug를 빠르게 수정할 수 있게 하는 것으로 'Edit and Continue'이라고도 합니다. code를 변경할 수 있도록 hot reload가 지원하는 code의 목록은 아래 link에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/visualstudio/debugger/supported-code-changes-csharp?view=vs-2022&quot;&gt;Supported Code Changes (C# and Visual Basic) - Visual Studio (Windows) | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1683978508373&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Supported Code Changes (C# and Visual Basic) - Visual Studio (Windows)&quot; data-og-description=&quot;Understand what code changes are supported when you are using the Edit and Continue feature while debugging a C# or Visual Basic project in Visual Studio.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/visualstudio/debugger/supported-code-changes-csharp?view=vs-2022&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/visualstudio/debugger/supported-code-changes-csharp&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b0Mfoe/hySBEb2ho6/n5zCbmBKKKo7335OKyU9Ik/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/visualstudio/debugger/supported-code-changes-csharp?view=vs-2022&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/visualstudio/debugger/supported-code-changes-csharp?view=vs-2022&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b0Mfoe/hySBEb2ho6/n5zCbmBKKKo7335OKyU9Ik/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Supported Code Changes (C# and Visual Basic) - Visual Studio (Windows)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Understand what code changes are supported when you are using the Edit and Continue feature while debugging a C# or Visual Basic project in Visual Studio.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6가 release 되기 전 Microsoft의 직원은 Hot reload를 Visual Studio 전용기능을 만들려고 시도했는데 Microsoft내 open-source관련 부서에서는 이와 같은 결정을 뒤집어 command-line에서도 사용가능하도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'csStudy04' solution에서 'HotReloading' project를 추가하고 Program.cs에서 기존 구문을 모두 제거한 뒤 2초마다 console로 message를 출력하는 아래 예제를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1683979315314&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;while (true)
{
	Console.WriteLine(&quot;Hello, Hot Reload!&quot;);
	await Task.Delay(2000);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Visual Studio 2022에서 Hot reload사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project를 실행하고 매2초마다 console에 출력되는 message를 확인합니다. 이 상태에서 Debug-&amp;gt;Apply Code Changes menu를 선택하거나 toolbar의 Hot Reload button을 click 하여 message를 'Hello to Goodbye'로 변경합니다. 그러면 message는 console app을 재시작하지 않고도 변경사항이 적용될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOMMg7/btsDxjVS4Ba/yF5kw1VGrGHrIyN03lemBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOMMg7/btsDxjVS4Ba/yF5kw1VGrGHrIyN03lemBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOMMg7/btsDxjVS4Ba/yF5kw1VGrGHrIyN03lemBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOMMg7%2FbtsDxjVS4Ba%2FyF5kw1VGrGHrIyN03lemBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;328&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'Hot Reload' button의 mene를 내려 'Hot Reload on File Save'를 check하면 source code의 file이 저장될 때마다 변경사항이 자동작으로 반영될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Visual Studio Code와 commnad line에서 Hot reload사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio Code에서는 console app을 실행할때 Hot Reload를 사용하기 위해서 특별한 명령을 내려야 합니다. Visual Studio Code의 TERMINAL이나 Windows Terminal을 열고 해당 project가 존재하는 folder로 이동한 뒤 'dotnet watch'명령을 내립니다. 그러면 다음과 같이 console app이 hot reload가 활성화되어 app이 실행될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kCm3V/btsDyNBXZyB/b8I2SijE7kXWrDpw2UKFh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kCm3V/btsDyNBXZyB/b8I2SijE7kXWrDpw2UKFh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kCm3V/btsDyNBXZyB/b8I2SijE7kXWrDpw2UKFh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkCm3V%2FbtsDyNBXZyB%2Fb8I2SijE7kXWrDpw2UKFh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1002&quot; height=&quot;193&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 이전과 마찬가지로 message를 'Hello to Goodbye'로 변경하여 2초후에 해당 변경내용이 적용되는지 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQHgrY/btsDxJNxdwb/qClMtkAWrAj2pdgTu2Axw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQHgrY/btsDxJNxdwb/qClMtkAWrAj2pdgTu2Axw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQHgrY/btsDxJNxdwb/qClMtkAWrAj2pdgTu2Axw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQHgrY%2FbtsDxJNxdwb%2FqClMtkAWrAj2pdgTu2Axw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;539&quot; height=&quot;406&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app의 실행을 중단하고자 한다면 'Ctrl + C' key를 누릅니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. 개발중에서와 runtime에서의 Logging&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;code에서 모든 bug가 제거되었다고 판단된다면 release version compile 하고 사람들이 사용할 수 있도록 application을 배포할 수 있을 것입니다. 하지만 그럼에도 불구하고 code상에는 잠재적인 bug가 존재할 수 있고 runtime동안에 예상하지 못한 에러가 유발될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 오류가 발생했을때를 기억하고 그때 자신이 무엇을 했는지에 대해 자세하게 설명하지 못하는 경우가 대부분입니다. 따라서 문제점을 파악하고 수정할 수 있도록 오류상황을 재현하기 위해 필요한 정보를 제공할 때 이러한 정보에 의존해서는 안됩니다. 대신 관련된 event를 기록함으로써 code에 대한 문제점을 측정할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무슨 일이 일어나고 있는지에 대한 log를 위해, 특히 예외가 발생했을 때를 위해 application전역에 관련 code를 추가합니다. 그리고 이러한 log를 검토하여 문제점을 추적하고 이를 해결하기 위해 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) logging option&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 logging기능을 추가함으로써 code를 측정하기 위한 몇몇 기본적인 방법을 포함하고 있습니다. 여기서 우리는 기본적인 부분만을 살펴볼 것입니다. 사실 logging은 제삼자가 Microsoft가 제공하는 것을 확장한 강력한 solution의 풍부한 생태계로 만들어져 온 영역입니다. 따라서 어떤 것이 필요한지는 필요한 상황에 따라 달라질 수 있으므로 특정한 것을 사용하라고 권장할 수는 없습니다. 그러나 대개 아래 항목의 logging을 많이 사용하는 것으로 보입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apache log4net&lt;/li&gt;
&lt;li&gt;NLog&lt;/li&gt;
&lt;li&gt;Serilog&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Debug와 Trace를 사용한 추적&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 code에 간단한 logging을 추가하기 위한 2가 유형이 존재하는데 그것이 바로 Debug와 Trace입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Debug : 개발하는 동안에 작성되는 logging을 위해 사용되는 class입니다.&lt;/li&gt;
&lt;li&gt;Trace : 개발과 runtime둘다 작성되는 logging을 위해 사용되는 class입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 예제를 통해 우리는 Console app에서 무엇인가를 출력하기 위해 WriteLine method를 사용했었습니다. Debug와 Trace class도 비슷하게 무엇인가를 출력하기 위해 사용되는데 이 출력의 대상이 trace listener가 되며 trace listener는 WriteLine method를 호출할 때 원하는 곳 어디든 출력을 쓰도록 구성할 수 있는 type입니다. .NET에서는 console로의 출력을 포함해 자체적으로 제공하는 몇몇 trace listener가 존재하고 있고 필요하다면 TraceListener type에서 상속하여 자신만의 listener를 직접 만들 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 기본 trace listener로 작성하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 trace listener인 DefaultTraceListener class가 자동적으로 구성되고 Visual Studio의 Debug window에 기록되면 code를 사용해 다른 trace listener를 구성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy04 solution에서 Instrumenting이름의 project를 추가하고 Program.cs에서 기존구문을 모두 제거한 뒤 System.Diagnostics namespace를 import 하고 뒤이어 Debug와 Trace class에서 message를 기록하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684111190447&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Diagnostics;

Debug.WriteLine(&quot;Debug says, I am watching!&quot;);
Trace.WriteLine(&quot;Trace says, I am watching!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서 View -&amp;gt; Output menu를 선택하고 'Show output from'에서 'Debug'를 선택한 다음 Instrumenting console app의 debugging을 시작합니다. Visual Studio 2022 Output window에서 assembly DLL의 loaded 다른 debugging정보와 함께 2개의 message가 표시됨을 볼 수 있을 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 34px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 34px;&quot;&gt;Debug&amp;nbsp;says,&amp;nbsp;I&amp;nbsp;am&amp;nbsp;watching! &lt;br /&gt;Trace&amp;nbsp;says,&amp;nbsp;I&amp;nbsp;am&amp;nbsp;watching!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) trace listener 구성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 text file에 기록하는 trace listener를 설정해 볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Debug와 Trace에서 WriteLine을 호출하기 전에 바탕화면으로 새로운 text file을 생성하고 해당 file로 log를 기록하는 trace listener를 전달하는 구문과 함께 buffer로 자동 flush 되도록 설정하는 code를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684115515588&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Diagnostics;

string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), &quot;log.txt&quot;);
Console.WriteLine($&quot;Writing to: {logPath}&quot;);

TextWriterTraceListener logFile = new(File.CreateText(logPath));
Trace.Listeners.Add(logFile);
Trace.AutoFlush = true;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;file을 나타내는 모든 type은 대게 성능향상을 위해 buffer를 구현합니다. file로 즉시 기록하는 대신 작성된 data를 memory buffer에 담아두고 buffer가 일정용량을 차지하게 되면 그때까지의 data전체를 file로 기록하는 것입니다. 이러한 동작으로 인해 예제를 실행할 때는 즉각적인 결과를 학인할 수 없을 수도 있습니다. 예제의 AutoFlush는 Flush method가 매번 기록 후에 호출되도록 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서는 toolbar에 있는 Solution Configrurations의 drop-down목록에서 Release를 선택하고 Debug-&amp;gt;Start Without Debugging menu를 선택해 예제를 실행합니다. 그런 뒤 바탕화면에서 log.txt file을 열어 'Trace says, I am watching!'문구가 기록되어 있는지 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;573&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YLU6o/btsDwDNP326/eR2tCKRPJsQq6mQoWlZkFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YLU6o/btsDwDNP326/eR2tCKRPJsQq6mQoWlZkFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YLU6o/btsDwDNP326/eR2tCKRPJsQq6mQoWlZkFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYLU6o%2FbtsDwDNP326%2FeR2tCKRPJsQq6mQoWlZkFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;573&quot; height=&quot;83&quot; data-origin-width=&quot;573&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Debug설정으로 app을 동작시킬 때는 Debug와 Trace가 활성화되어 trace listener로 기록을 수행할 것입니다. 반면 Release설정으로 app이 동작될 때는 오로지 Trace만 trace listener로 기록을 시도합니다. 따라서 Debug.WriteLine의 호출은 application을 release version으로 build 하게 되면 자동으로 제거되고 따라서 성능에 영향을 주지 않을 것이므로 code전역에서 자유롭게 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) trace 수준 전환하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Trace.WriteLine를 호출하는 것은 release이후에도 code에 남아있으며 이들에 대한 output은 trace switch르 통해 어느 정도 제어할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;trace switch의 값은 숫자나 단어로 설정할 수 있습니다. 예를 들어 숫자 3은 아래 표에서와 같이 Info라는 단어로 바뀔 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6356%;&quot;&gt;Number&lt;/td&gt;
&lt;td style=&quot;width: 33.1008%;&quot;&gt;Word&lt;/td&gt;
&lt;td style=&quot;width: 49.2635%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6356%;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 33.1008%;&quot;&gt;Off&lt;/td&gt;
&lt;td style=&quot;width: 49.2635%;&quot;&gt;아무것도 출력하지 않습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6356%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 33.1008%;&quot;&gt;Error&lt;/td&gt;
&lt;td style=&quot;width: 49.2635%;&quot;&gt;Error만 출력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6356%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 33.1008%;&quot;&gt;Warning&lt;/td&gt;
&lt;td style=&quot;width: 49.2635%;&quot;&gt;Error와 Warning만 출력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6356%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 33.1008%;&quot;&gt;Info&lt;/td&gt;
&lt;td style=&quot;width: 49.2635%;&quot;&gt;Error와 Warning, Infomation만 출력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6356%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 33.1008%;&quot;&gt;verbose&lt;/td&gt;
&lt;td style=&quot;width: 49.2635%;&quot;&gt;전체 수준을 모두 출력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;trace switch를 사용하기 위해서는 우선 필요한 NuGet package를 설치하여 JSON appsettings file로부터 구성설정값을 load 할 수 있도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Project에 package 추가하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio의 project에서 mouse 오른쪽 button을 눌러 'Manage NuGet Packages'를 선택합니다. 그리고 Browse tab을 선택한 뒤 'Microsoft.Extensions.Configuration'을 입력하고 아래 항목의 NuGet package를 검색해 설치합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Microsoft.Extensions.Configuration.Binder&lt;/li&gt;
&lt;li&gt;Microsoft.Extensions.Configuration.Json&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kARAe/btsDCTpnG0c/HwQhd3u7JEeVhkJ4JWkFck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kARAe/btsDCTpnG0c/HwQhd3u7JEeVhkJ4JWkFck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kARAe/btsDCTpnG0c/HwQhd3u7JEeVhkJ4JWkFck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkARAe%2FbtsDCTpnG0c%2FHwQhd3u7JEeVhkJ4JWkFck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1444&quot; height=&quot;798&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;798&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이외에도 XML file이나 INI, 환경 변수, command line에서 설정을 loading 하는 package도 존재합니다. 따라서 project의 환경설정에 가장 적합한 것을 사용하면 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Project package 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NuGet package가 설치되고 나면 project file의 &amp;lt;ItemGroup&amp;gt; section에서 다음과 같이 참조를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684199060218&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
	&amp;lt;PackageReference Include=&quot;Microsoft.Extensions.Configuration.Binder&quot; Version=&quot;8.0.1&quot; /&amp;gt;
	&amp;lt;PackageReference Include=&quot;Microsoft.Extensions.Configuration.Json&quot; Version=&quot;8.0.0&quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 project에 'appsettings.json'이름의 file을 추가하고 LogSwitch이름의 설정을 Level값과 함께 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684199303012&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;LogSwitch&quot;: {
        &quot;Value&quot;: &quot;Info&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Microsoft는 .NET 7의 최종 release이후 7.0.3&amp;nbsp; version의 Microsoft.Extensions.Configuration.Binder package에 존재해 있던 bug를 수정하였습니다. 이 bug는 이전 version에서 설정을 읽는 방식때문에 예외가 발생할 수 있습니다. Microsoft.Extensions.Configuration.Binder 7.0.3이전까지는 Level속성을 &quot;Level&quot;: &quot;Info&quot;처럼 설정하는게 가능했지만 bug가 수정된 이후에는 내부 class에서 Value속성이 설정되어야 하기 때문에&amp;nbsp; 예외를 발생시킬 수 있으므로 Value속성을 설정하거나 아니면 둘의 속성값 모두를 설정해야 합니다.&lt;br /&gt;&lt;br /&gt;https://github.com/dotnet/runtime/issues/82998&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가된 appsettings.json file에서 mouse오른쪽 button을 눌러 'Properties'를 선택한 다음 'Copy to Output Directory'설정을 'Copy if newer'로 변경합니다. 이 설정은 project folder에서 project의 console app을 동작시키는 Visual Studio Code와는 달리 Instrumenting\bin\Debug\net7.0 또는 Instrumenting\ bin\Release\net7.0에서 project app을 동작시키게 되므로 필요한 설정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 다음과 같이 Microsoft.Extensions.Configuration를 import 하고&lt;/p&gt;
&lt;pre id=&quot;code_1684199586690&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.Extensions.Configuration;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;file의 끝에서는 아래와 같은 구문을 추가합니다. 해당 문은 현재 folder에서 appsettings.json file을 확인하기 위한 configuration builder를 생성하고 설정을 build 하며 trace switch를 생성한 후 여기에 설정을 binding 함으로써 level을 설정하고 4개의 switch level을 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684200227961&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;Reading from appsettings.json in {0}&quot;, arg0: Directory.GetCurrentDirectory());

ConfigurationBuilder builder = new();
builder.SetBasePath(Directory.GetCurrentDirectory());
builder.AddJsonFile(&quot;appsettings.json&quot;, optional: true, reloadOnChange: true);

IConfigurationRoot configuration = builder.Build();

TraceSwitch ts = new(displayName: &quot;LogSwitch&quot;, description: &quot;This switch is set via a JSON config.&quot;);

configuration.GetSection(&quot;LogSwitch&quot;).Bind(ts);

Trace.WriteLineIf(ts.TraceError, &quot;Trace error&quot;);
Trace.WriteLineIf(ts.TraceWarning, &quot;Trace warning&quot;);
Trace.WriteLineIf(ts.TraceInfo, &quot;Trace information&quot;);
Trace.WriteLineIf(ts.TraceVerbose, &quot;Trace verbose&quot;);

Console.ReadLine();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bind구문에 breakpoint를 설정하고 debugging을 시작합니다. debugging이 시작된 뒤 Locals window에서 ts변수식을 확장해 보면 다음과 같이 Level과 TraceError, TraceWarning 등이 모두 false로 되어 있음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZhSwz/btsDA5BOM6T/oEk6KH4NQTNpZBY2x9Hnxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZhSwz/btsDA5BOM6T/oEk6KH4NQTNpZBY2x9Hnxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZhSwz/btsDA5BOM6T/oEk6KH4NQTNpZBY2x9Hnxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZhSwz%2FbtsDA5BOM6T%2FoEk6KH4NQTNpZBY2x9Hnxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 Step Into 또는 Step Over button을 누르거나 F11 또는 F10 key를 눌러 Bind method를 호출하여 ts 변수식에서 level이 Info로 변경되는 것을 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 4개의 Trace.WriteLineIf의 호출 부분을 실행함으로써 Info로 변경된 모든 level이 Verbose를 제외하고 Output - Debug window에 표시됨을 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EFMar/btsDBzJscBj/xUduJiZ6EKrkzJpB3kvZV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EFMar/btsDBzJscBj/xUduJiZ6EKrkzJpB3kvZV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EFMar/btsDBzJscBj/xUduJiZ6EKrkzJpB3kvZV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEFMar%2FbtsDBzJscBj%2FxUduJiZ6EKrkzJpB3kvZV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Debugging을 중지하고 이번에는 appsettings.json에서 'Value'의 값을 2(warning)로 변경한 뒤 'Solution Configurations'의 설정을 Release로 선택하고 Debug -&amp;gt; Start Without Debugging으로 console app을 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 바탕화면의 log.txt file을 열어보면 4개의 trace level 중에서 trace error와 warning level만이 출력되어 있음을 확인할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Trace&amp;nbsp;says,&amp;nbsp;I&amp;nbsp;am&amp;nbsp;watching! &lt;br /&gt;Trace&amp;nbsp;error &lt;br /&gt;Trace&amp;nbsp;warning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 어떠한 인수도 전달하지 않았다면 기본 trace switch level은 0(OFF)이 되고, 그러면 어떠한 switch level로 출력되지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) source code에 관한 Logging 정보&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;log를 사용할 때 때로는 source code file의 이름이나 method이름, 혹은 line number와 같은 source code의 정보를 포함하고 싶을 수 있습니다. 심지어 C# 10부터는 함수의 인수로 전달된 모든 수식을 문자열값으로 가져올 수 있으므로 이를 log화 하는 것도 가능하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 모든 정보는 함수의 매개변수에 아래 table에 나타난 특별한 attribute를 적용함으로써 가져올 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 134px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 35.5814%; height: 40px;&quot;&gt;[CallerMemberName] string member = &quot;&quot;&lt;/td&gt;
&lt;td style=&quot;width: 64.4186%; height: 40px;&quot;&gt;member라는 이름의 string 매개변수에 매개변수가 정의된 해당 method를 실행하는 method나 property이름을 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 60px;&quot;&gt;
&lt;td style=&quot;width: 35.5814%; height: 60px;&quot;&gt;[CallerFilePath] string filepath = &quot;&quot;&lt;/td&gt;
&lt;td style=&quot;width: 64.4186%; height: 60px;&quot;&gt;filepath라는 이름의 string 매개변수에 매개변수가 정의된 해당 method를 실행하는 구문을 포함한 source code file의 이름을 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 35.5814%; height: 17px;&quot;&gt;[CallerLineNumber] int line = 0&lt;/td&gt;
&lt;td style=&quot;width: 64.4186%; height: 17px;&quot;&gt;line이라는 int 매개변수에 매개변수가 정의된 해당 method를 실행하는 구문을 가진 source code file의 line번호를 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 35.5814%; height: 17px;&quot;&gt;[CallerArgumentExpression( nameof(argumentExpression))] string expression = &quot;&quot;&lt;/td&gt;
&lt;td style=&quot;width: 64.4186%; height: 17px;&quot;&gt;expression이라는 string 매개변수에 argumentExpression이름의 매개변수로 전달된 식을 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 매개변수는 기본값을 할당하여 선택적 매개변수로 만들어야 합니다. 그럼 예제를 통해 실제 어떻게 해당 attribute를 사용할 수 있는지 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instrumenting project에서 Methods.cs이름의 class file을 추가하고 해당 file에 LogSourceDetails이름의 함수를 정의합니다. 이 함수는 호출 code에 대한 정보를 log 하기 위해 위에서 설명한 4개의 attribute를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684225649039&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	static void LogSourceDetails(bool condition, [CallerMemberName] string member = &quot;&quot;, [CallerFilePath] string filepath = &quot;&quot;, [CallerLineNumber] int line = 0, [CallerArgumentExpression(nameof(condition))] string expression = &quot;&quot;)
	{
		Trace.WriteLine(string.Format(&quot;[{0}]\n {1} on line {2}. Expression: {3}&quot;, filepath, member, line, expression));
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 file의 마지막 Console.ReadLine()를 호출하기 전에 LogSourceDetails함수로 전달될 식에서 사용되는 변수를 선언하고 값을 할당하는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684287697489&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Trace.WriteLineIf(ts.TraceVerbose, &quot;Trace verbose&quot;);

int unitsInStock = 12;
LogSourceDetails(unitsInStock &amp;gt; 10);

Console.ReadLine();&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서는 고정된 값의 식을 사용하지만 실제 project에서는 사용자에 의해 동적으로 생성되는 식이 될 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;debugging 없이 예제를 실행한 뒤 console app을 닫고 바탕화면의 log.txt를 보면 다음과 같이 file이 기록되어 있음을 확인할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;[C:\Users\clicl\source\repos\csStudy04\Instrumenting\Program.cs]&lt;br /&gt;&amp;nbsp;&amp;lt;Main&amp;gt;$&amp;nbsp;on&amp;nbsp;line&amp;nbsp;32.&amp;nbsp;Expression:&amp;nbsp;unitsInStock&amp;nbsp;&amp;gt;&amp;nbsp;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. 단위 TEST&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완성된 application에서 오류를 수정하기보다는 가급적 개발과정에서 오류를 발견하는 것이 훨씬 좋다는 건 두말할 나위가 없을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 단위 test라는 것이 존재하며 개발 중에서 bug를 찾기 위한 좋은 방법입니다. 어떤 개발자는 실제 code를 작성하기 전에 단위 test를 먼저 생성하는 원칙을 따르고 있으며 이러한 개발 방법을 TDD(Test-Driven Development)라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 단위 test를 위한 framework로 MSTest를 제공하고 있으며 NUnit라는 또 다른 framework도 존재합니다. 여러 가지 단위 test framework를 사용할 수 있지만 예제에서는 xUnit.net을 사용할 것입니다. 이 framework는 무료이며 open-source입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Testing type&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 test는 test를 위한 여러 유형 중의 하나이며 아래 표와 같은 다른 test 유형도 존재합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;Unit&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;code의 작은 단위에 대한 test로서 method나 function과 같은 것입니다. 단위 test는 필요한 경우 이들을 mocking함으로서 의존성으로 부터 격리된 code의 단위를 수행합니다. 일반적인 입력과 예상되는 출력, 한계 test를 위한 극한입력, 잘못된 입력을 통한 예외처리 test등 여러 test를 가질 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;Integration&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;작은 단위와 큰 component가 하나의 software로서 작동할 수 있는지를 test하는 것으로 source code가 없는 외부 component와의 통합을 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;Software가 작동할 전체 system 환경을 test합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;Performance&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;Software의 성능을 test하는 것으로 수초이내에 data가 채워진 web page를 반환해야 하는 경우등을 들 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;Load&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;얼마나 많은 요청을 software가 필요한 성능을 유지하면서 동시적으로 처리할 수 있는가에 대한 test로서 10000명의 사용자가 동시에 방문하는 경우등을 들 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.186%;&quot;&gt;User Acceptance&lt;/td&gt;
&lt;td style=&quot;width: 80.814%;&quot;&gt;사용자가 software를 사용하여 만족스럽게 필요한 작업을 수행할 수 있는지의 여부를 test합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) test를 수행하는 class library 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 test를 수행하는 함수를 만들어야 합니다. 이를 위해 기존 예제의 console app project와는 별개의 class library project를 생성해야 하며 해당 class library는 다른 .NET application에서 참조되고 배포될 수 있는 code의 package입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy04 solution에서 'CalculatorLib'이름의 class library project를 생성하고 'Class1.cs'이름의 file을 ' Calculator.cs'로 변경한 후 해당 file을 아래와 같이 작성합니다. (고의적으로 잘못된 code를 사용하는 것이므로 있는 그대로 작성합니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1684308232911&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace CalculatorLib
{
	public class Calculator
	{
		public double Add(double a, double b)
		{
			return a * b;
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 해당 project를 build 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 csStudy04 soluction에 'CalculatorLibUnitTests'이름의 xUnit Test Project를 추가한 뒤 해당 project에서 위에서 만든 CalculatorLib를 참조추가하고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crDtLe/btsDuTpJ62D/EvFKKeKXeMyKEliOIkAyQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crDtLe/btsDuTpJ62D/EvFKKeKXeMyKEliOIkAyQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crDtLe/btsDuTpJ62D/EvFKKeKXeMyKEliOIkAyQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrDtLe%2FbtsDuTpJ62D%2FEvFKKeKXeMyKEliOIkAyQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;279&quot; height=&quot;225&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 project를 build 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 단위 test 작성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 test는 세 부분으로 구성됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Arrange : 입력과 출력을 위한 변수를 선언하고 instance 하는 부분입니다.&lt;/li&gt;
&lt;li&gt;Act : test 할 단위를 실행하는 부분입니다. 지금의 경우에는 test할 method를 호출하는 것이 됩니다.&lt;/li&gt;
&lt;li&gt;Assert : 출력에 관한 하나 또는 그 이상의 assertion을 만드는 부분으로 assertion은 결과가 true가 아니면 test는 실패했음을 표시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Calculator class에 대한 몇 가지 단위 test를 만들어볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 'UnitTest1.cs'file을 'CalculatorUnitTests.cs'로 변경하고 file안에서 CalculatorLib namespace를 import 합니다. 그리고 CalculatorUnitTests class에 2개의 method를 추가할 텐데 하나는 2와 2를 더하는 것이고 다른 하나는 2와 3을 더하는 method입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684310299670&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using CalculatorLib;

namespace CalculatorLibUnitTests
{
	public class CalculatorUnitTests
	{
		[Fact]
		public void TestAdding2And2()
		{
			// arrange 
			double a = 2;
			double b = 2;
			double expected = 4;
			Calculator calc = new();

			// act
			double actual = calc.Add(a, b);

			// assert
			Assert.Equal(expected, actual);
		}

		[Fact]
		public void TestAdding2And3()
		{
			// arrange 
			double a = 2;
			double b = 3;
			double expected = 5;
			Calculator calc = new();

			// act
			double actual = calc.Add(a, b);

			// assert
			Assert.Equal(expected, actual);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 단위 test 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에까지 진행하면 이제 단위 test를 실행할 준비가 완료된 것입니다. Visual Studio에서 Test -&amp;gt; Run All Tests menu를 선택합니다. 그러면 Test Explorer에서 2개의 test동작에 대한 결과를 표시할 텐데 아마도 하나는 성공(passed), 다른 하나는 실패(Failed)로 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcXEDW/btsDuRFoKSu/0kTgjfdACWb6M2O0JDzsj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcXEDW/btsDuRFoKSu/0kTgjfdACWb6M2O0JDzsj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcXEDW/btsDuRFoKSu/0kTgjfdACWb6M2O0JDzsj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcXEDW%2FbtsDuRFoKSu%2F0kTgjfdACWb6M2O0JDzsj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;895&quot; height=&quot;395&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● bug 수정하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add method에서 a * b부분을 a + b로 변경하고 다시 test를 실행합니다. 그러면 이번에는 모든 method가 test에 통과했음을 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD29lT/btsDAu9HX6M/d6bEVIKjhs1KssWBDxdI90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD29lT/btsDAu9HX6M/d6bEVIKjhs1KssWBDxdI90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD29lT/btsDAu9HX6M/d6bEVIKjhs1KssWBDxdI90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD29lT%2FbtsDAu9HX6M%2Fd6bEVIKjhs1KssWBDxdI90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;375&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. 함수에서의 예외 throwing과 catching&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 처리에 관해서는 아래 글에서 try-catch문을 사용해 본 바 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 3. 흐름제어, Type 변환, 예외 처리&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1706579101512&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 3. 흐름제어, Type 변환, 예외 처리&quot; data-og-description=&quot;이번에는 변수를 사용한 간단한 동작과 분기, pattern matching 수행, 구문 혹은 block의 반복과 여러 값을 저장하기 위한 array, 특정 type에서 다른 type으로의 변수나 표현식에 대한 변환, 예외 처리 그&quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4OL4B/hyVb4zeZXV/T6N0xWBDuaHldpiUtrBu30/img.png?width=800&amp;amp;height=50&amp;amp;face=0_0_800_50,https://scrap.kakaocdn.net/dn/cDUl13/hyVb1if7fL/bE4iwYWkbroLMpRB3s1kS1/img.png?width=800&amp;amp;height=50&amp;amp;face=0_0_800_50,https://scrap.kakaocdn.net/dn/A16Rk/hyVb7JxjQu/KHfxggfGBT4kC97Cf8g1Ek/img.png?width=979&amp;amp;height=220&amp;amp;face=0_0_979_220&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12-%EC%99%80-NET-8-3-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4OL4B/hyVb4zeZXV/T6N0xWBDuaHldpiUtrBu30/img.png?width=800&amp;amp;height=50&amp;amp;face=0_0_800_50,https://scrap.kakaocdn.net/dn/cDUl13/hyVb1if7fL/bE4iwYWkbroLMpRB3s1kS1/img.png?width=800&amp;amp;height=50&amp;amp;face=0_0_800_50,https://scrap.kakaocdn.net/dn/A16Rk/hyVb7JxjQu/KHfxggfGBT4kC97Cf8g1Ek/img.png?width=979&amp;amp;height=220&amp;amp;face=0_0_979_220');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 3. 흐름제어, Type 변환, 예외 처리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 변수를 사용한 간단한 동작과 분기, pattern matching 수행, 구문 혹은 block의 반복과 여러 값을 저장하기 위한 array, 특정 type에서 다른 type으로의 변수나 표현식에 대한 변환, 예외 처리 그&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 문제를 완화하기 위한 충분한 정보를 가지고 있는 경우에만 예외를 catch 하고 처리해야 합니다. 그럴 수 없는 경우에 예외는 상위 수준으로 호출 stack을 통해 전달되도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) usage error와 execution error&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Usage error는 개발자가 함수를 잘못 사용하는 경우와 같은 것이며 일반적으로 매개변수로 잘못된 값을 전달하는 경우를 들 수 있습니다. 이런 경우에는 개발자가 정확한 값을 전달하도록 code를 변경함으로써 처리할 수 있습니다. programming을 처음 접하는 경우에는 모든 error가 usage error라고 추정함으로써 예외를 피할 수 있을 것이라 생각하기도 하지만 실제로 그렇게 하는 것은 불가능하며 Usage error는 application이 제품화되어 실행되기 전에 모두 수정되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Execution error는 application이 code를 보완하여 수정할 수 없는 실행 시에 발생하는 것입니다. Execution error는 세부적으로 program error와 system error로 분리될 수 있습니다. 만약 application에서 network resource에 접근하려 할 때 network가 down 된 상태라면 예외를 기록하고 잠시 기다렸다 다시 시도함으로써 예외를 처리할 수 있을 것입니다. 하지만 memory부족과 같은 system error의 경우에는 처리가 쉽지 않을 수 있습니다. 존재하지 않는 file을 열려고 하는 경우에는&amp;nbsp; 이에 대한 error를 catch 하고 program상에서 신규 file을 생성함으로써 처리할 수 있습니다. Program error는 code를 개선함으로써 수정할 수 있지만 System error는 종종 code의 개선만으로 해결할 수 없는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 함수에서의 일반적인 예외&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔한 경우는 아니지만 usage error를 나타내기 위해 새로운 예외 type을 정의하는 경우도 있으며 .NET은 이미 사용가능한 많은 type을 정의하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수와 함께 함수를 정의할 때 code에서는 매개변수의 값을 확인하고 적절하게 작동할 수 없도록 하는 값을 가진 경우 예외를 발생시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 함수의 인수가 null이라면 ArgumentNullException를 발생시킬 수 있을 것입니다. 그 외 다른 문제라면 ArgumentException, NotSupportedException 또는 InvalidOperationException을 발생시킬 수도 있고 어떠한 문제든 아래와 같은 code를 통해 문제점을 설명하는 message로 사람들(일반적으로 class library나 함수에 대한 개발자 또는 GUI app에서 최상위 수준인 경우 최종 사용자)에게 상세내용을 제공해 줄 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684378772649&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Withdraw(string accountName, decimal amount)
{
	if (accountName is null)
	{
		throw new ArgumentNullException(paramName: nameof(accountName));
	}
	if (amount &amp;lt; 0)
	{
		throw new ArgumentException(message: $&quot;{nameof(amount)} cannot be less than zero.&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 exception가 발생할 때 if문을 사용하는 대신 .NET 6부터는 인수가 null인 경우 예외를 발생시키는 편리한 method를 도입하였고 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684378957177&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Withdraw(string accountName, decimal amount)
{
	ArgumentNullException.ThrowIfNull(accountName);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 11 preview에서는 null 확인 연산자인!! 을 도입하여 위에서와 같은 기능을 수행하도록 하였으나 정식 version에서 제거되었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684379075450&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Withdraw(string accountName!!, decimal amount)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 usage error를 잡기 위해서는 꼭 try-catch문을 사용할 필요는 없습니다. 이러한 예외로 인해 함수를 호출하는 개발자는 문제를 방지하기 위해 application이 배포되기 전 code를 수정해야 합니다. 이는 code가 usage error type의 예외를 발생시킬 필요가 없다는 것을 의미하는 것이 아니지만 다른 개발자들이 함수를 정확히 호출할 수 있도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) call stack&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET console application의 주요 진입점은 Program class의 Main(명시적으로 method를 정의한 경우, top-level program기능으로 만들어진 것이라면 &amp;lt;Main&amp;gt;$입니다.)이라는 이름의 method입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Main method는 다른 method를 호출할 것이고 해당 method는 또 다른 method를 호출할 수 있을 것입니다. 그리고 이들 method는 현재 project나 참조된 project 또는 NuGet package에 존재할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bos9J2/btsDA4iCdps/KSvAIub7juaBGaaGUQagy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bos9J2/btsDA4iCdps/KSvAIub7juaBGaaGUQagy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bos9J2/btsDA4iCdps/KSvAIub7juaBGaaGUQagy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbos9J2%2FbtsDA4iCdps%2FKSvAIub7juaBGaaGUQagy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;978&quot; height=&quot;156&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위 그림과 비슷한 method chain을 만들어 봄으로서 예로서 제시된 예외를 catch 하고 이를 적절한 처리하는 방법에 관해 알아볼 것입니다. csStudy04 solution에서 'CallStackExceptionHandlingLib'이름의 Class Library project를 추가하고 Class1.cs file을 Calculator.cs로 변경한 뒤 Calculator.cs를 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684460827110&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace CallStackExceptionHandlingLib
{
	public class Calculator
	{
		public static void Gamma() // public so it can be called from outside
		{
			Console.WriteLine(&quot;In Gamma&quot;);
			Delta();
		}
		private static void Delta()
		// private so it can only be called internally
		{
			Console.WriteLine(&quot;In Delta&quot;);
			File.OpenText(&quot;bad file path&quot;);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 같은 solution에 Console App형식의 'CallStackExceptionHandling' project를 추가하고 해당 project에서 CallStackExceptionHandlingLib를 참조 추가합니다. 그리고 Program.cs에서 기존 구문을 삭제하고 2개의 method를 정의한 뒤 아래와 같이 class library의 method와 이들 method를 연쇄적으로 호출하도록 하는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684461246669&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using CallStackExceptionHandlingLib;
using static System.Console;

WriteLine(&quot;In Main&quot;);
Alpha();

void Alpha()
{
	WriteLine(&quot;In Alpha&quot;);
	Beta();
}

void Beta()
{
	WriteLine(&quot;In Beta&quot;);
	Calculator.Gamma();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 debugging 없이 project를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHYJlf/btsDBxdNRsN/ZFEj7IdV4MWNWsdNC4Qbck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHYJlf/btsDBxdNRsN/ZFEj7IdV4MWNWsdNC4Qbck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHYJlf/btsDBxdNRsN/ZFEj7IdV4MWNWsdNC4Qbck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHYJlf%2FbtsDBxdNRsN%2FZFEj7IdV4MWNWsdNC4Qbck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1017&quot; height=&quot;544&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call stack은 밑에서부터 거슬러 올라가야 하는데 이를 통해 우리는 다음과 같은 사항을 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처음 호출되는 것은 Program class에서 자동으로 생성된 &amp;lt;Main&amp;gt;$ entry point입니다. 여기에서 string배열을 통해 인수가 전달됩니다.&lt;/li&gt;
&lt;li&gt;두 번째로 호출되는 것은 '&amp;lt;&amp;lt;Main&amp;gt;$&amp;gt;g_Alpha|0_0'함수입니다.(C# compiler는 Alpha를 지역함수로 추가할 때 이름을 바꾸게 됩니다.)&lt;/li&gt;
&lt;li&gt;세 번째로 호출되는 것은 Beta 함수입니다.&lt;/li&gt;
&lt;li&gt;네 번째로 호출되는 것은 Gamma 함수입니다.&lt;/li&gt;
&lt;li&gt;다섯 번째로 호출되는 것은 Delta 함수입니다. 이 함수에서는 옳지 않은 경로를 전달하여 file열기를 시도하는데 이때 예외를 발생시키게 됩니다. 이때 try-catch를 가진 모든 함수는 이 예외를 잡아낼 것입니다. 그렇지 않다면 위 결과와 같이 call stack의 상세와 함께 예외를 표시하게 되는 최상위에 도달할 때까지 자동적으로 call stack의 위로 예외를 전달하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;debugging 하기 위한 code의 단계적 실행이 필요하지 않다면 code를 debugger 없이 실행할 수 있습니다. 이 경우 debugger를 연결하지 않는 것이 특히 중요합니다. 그러면 예외가 catch 되고 이를 위 결과와 같이 표시하는 대신 GUI dialog box를 통해 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 예외를 catch 할 수 있는 곳&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 입장에서는 처리가 실패한 곳에서 예외를 catch 할지, call stack의 상위에서 집중적으로 예외를 catch 할지를 결정할 수 있습니다. 이는 통해 code를 단순화시키고 표준화시키는데 도움이 됩니다. 예외를 호출하면 하나이상의 예외유형이 발생할 수 있지만 call stack의 현재 지점에서 이들을 처리할 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 예외 재발생&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 예외를 catch 하여 이를 기록하고 해당 예외를 다시 발생시키고 싶은 경우도 있습니다. 예를 들어 application에서 호출될 저수준 class library를 작성하는 경우 해당 code에서는 error를 수정하기에 충분한 정보를 가지지 못할 수 있지만 이를 호출하는 application에서는 더 많은 정보를 가지고 있을 수 있습니다. 따라서 code에서는 error가 발생한 경우의 log를 기록하고 call stack의 상위로 예외를 던져 호출 application에서 이를 처리할지를 결정하도록 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 catch block안에서는 예외를 던지는 3가지 방법이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본래 call stack에서 catch 된 예외를 다시 발생시키기 위해 throw를 호출합니다.&lt;/li&gt;
&lt;li&gt;call stack의 현재 수준에서 예외가 발생한 것처럼 catch 된 예외를 발생시키려면 catch된 예외와함께 'throw ex'처럼&amp;nbsp; throw를 호출합니다. 그러나 이는 debugging을 위해 필요한 정보를 잠재적으로 잃어버릴 수 있기 때문에 좋지 않은 방법이지만 예외(Exception)에서 민감한 정보를 포함하고 있는 경우 이를 고의적으로 삭제하기 위해서는 유용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;호출자가 문제를 이해하는데 도움이 될 수 있는 message같이 더 많은 정보를 포함할 수 있는 다른 예외로 catch 된 예외를 wrap합니다. 이를 새로운 예외를 발생시키고 catch된 예외를&amp;nbsp; innerException 매개변수로서 전달합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 Gamma function을 호출할 때 예외가 발생했다면 해당 예외를 catch 하고 예외를 다시 발생시키기 위한 위의 3가지 방법 중 하나를 아래와 같이 수행할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684477647128&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void Beta()
{
	WriteLine(&quot;In Beta&quot;);

	try {
		Calculator.Gamma();
	}
	catch(Exception ex)
	{
		WriteLine(ex.Message.ToString());
		throw ex;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 code를 실제 작성하면 throw ex밑에 녹색 밑줄이 표시되면서 call stack 정보를 손실할 수 있다는 경고를 표시할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 call stack에 대한 일부 상세정보를 제외한 결과를 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5eNpy/btsDA4iCdpK/k5GOBnWpUXLCCLpUpuGwKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5eNpy/btsDA4iCdpK/k5GOBnWpUXLCCLpUpuGwKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5eNpy/btsDA4iCdpK/k5GOBnWpUXLCCLpUpuGwKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5eNpy%2FbtsDA4iCdpK%2Fk5GOBnWpUXLCCLpUpuGwKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 throw ex를 throw로만 변경하고 다시 project를 실행하면 이번에는 call stack의 모든 상세를 포함한 결과를 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) tester-doer pattern 구현하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tester-doer pattern은 일부 예외의 발생을 방지할 수 있습니다.(완전히 없애는 것이 아닙니다.) 이 pattern은 2개의 함수를 함께 사용하는 것으로 하나는 test를 수행하고 다른 하나는 test를 통과하지 못했다면 실패할 동작을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 이러한 pattern을 자체적으로 구현하고 있습니다. 예를 들어 Add method를 호출하여 collection에 item을 추가하기 전에 이것이 읽기 전용인지를 확인하는 test를 수행함으로써 Add method가 실패되고 예외를 발생시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 예로 은행에서 돈을 출금하기 전 초과인출여부를 확인하는 test를 아래와 같이 수행하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684479024703&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!bankAccount.IsOverdrawn())
{
	bankAccount.Withdraw(amount);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● tester-doer patten의 문제점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tester-doer pattern은 성능 overhead를 일으킬 수 있습니다. 따라서 test와 작동함수를 하나의 단일 함수로 결합한 try pattern을 구현할 수도 있는데 TryParse가 바로 이러한 방식으로 구현된 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tester-doer pattern의 또 다른 문제점은 다중 thread를 사용할 때 발생합니다. 이러한 경우 하나의 thread에서는 test함수를 호출하고 문제가 없음을 반환할 수 있습니다. 하지만 그때 다른 thread가 상태변경을 시도하는 것입니다. 그러면 원래 thread는 모든 것이 문제가 없다고 판단하고 자신의 실행을 계속 수행하지만 사실은 그렇지 않은 것입니다. 이러한 경우를 race condition이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 자체 try pattern 함수를 구현했다면 해당 함수의 실행이 실패하는 경우를 대비해 해당 type의 기본값으로 out 매개변수를 설정하고 false를 반환하는 것이 좋습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684479980119&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static bool TryParse(string? input, out Person value)
{
	if (Failure)
	{
		value = default(Person);
		return false;
	}
	
	// 성공한 경우
	value = new Person() { ... };
	return true;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>.NET/C#</category>
      <category>.NET 8</category>
      <category>C# 12</category>
      <category>catching</category>
      <category>Debugging</category>
      <category>exception</category>
      <category>hot reload</category>
      <category>logging</category>
      <category>testing</category>
      <category>throwing</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/594</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-4-Debuging%EA%B3%BC-Testing#entry594comment</comments>
      <pubDate>Mon, 22 Jan 2024 11:11:58 +0900</pubDate>
    </item>
  </channel>
</rss>