성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# 12 - 기본 생성자(Primary Constructors)</h1> <p> 이번엔 <a target='tab' href='https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md#c-next'>문서상</a> 2번째로 구현된 기본 생성자(Primary Constructors)에 대해 살펴보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Primary constructors for non-record classes and structs (<a target='tab' href='https://github.com/dotnet/csharplang/issues/2691'>Primary Constructors</a>) ; <a target='tab' href='https://devblogs.microsoft.com/dotnet/check-out-csharp-12-preview/#primary-constructors-for-non-record-classes-and-structs'>https://devblogs.microsoft.com/dotnet/check-out-csharp-12-preview/#primary-constructors-for-non-record-classes-and-structs</a> C#12 클래스 및 구조체 기본 생성자 | Patrick Smacchia ; <a target='tab' href='https://forum.dotnetdev.kr/t/c-12-patrick-smacchia/6837'>https://forum.dotnetdev.kr/t/c-12-patrick-smacchia/6837</a> </pre> <a name='preview'></a> <br /> <strike>아쉽게도 현재 시점(2023-05-06)의 Visual Studio 2022 정식 버전에서는 테스트할 수 없으며, Visual Studio 2022 Preview 버전을 다운로드해 프로젝트의 언어 속성을 preview로 변경해야 합니다.</strike> 2023-05-17 정식 버전이 17.6으로 나오면서 테스트할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <span style='color: blue; font-weight: bold'><LangVersion>preview</LangVersion></span> </PropertyGroup> </Project> </pre> <br /> <strike>(이후에 소개할 C# 12의 모든 기능은 Preview 버전이 필요합니다.)</strike><br /> <br /> <hr style='width: 50%' /><br /> <br /> 생성자 메서드는, 많은 역할을 수행할 수도 있지만 단순히 타입 내부의 상태 초기화만 하는 경우도 있습니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PersonDTO p = new PersonDTO("John", 42); public class PersonDTO { public PersonDTO(<span style='color: blue; font-weight: bold'>string name, int age</span>) { Name = name; Age = age; } public string Name { get; set; } public int Age { get; set; } } </pre> <br /> 이런 패턴의 경우, 코드를 단순화하기 위해 별도의 예약어를 추가한 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12392'>record</a>도 나오고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PersonDTO p = new PersonDTO("John", 42); public <span style='color: blue; font-weight: bold'>record class</span> PersonDTO(string Name, int Age); // 또는 public <span style='color: blue; font-weight: bold'>record struct</span> PersonDTO(string Name, int Age); </pre> <br /> <a target='tab' href='https://www.sysnet.pe.kr/2/0/13123'>required</a> 예약어도 나왔지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PersonDTO p = new PersonDTO { Name = "John", Age = 42 }; public class PersonDTO { public <span style='color: blue; font-weight: bold'>required</span> string Name { get; init; } public <span style='color: blue; font-weight: bold'>required</span> int Age { get; init; } } </pre> <br /> 만족할 수 없었나 봅니다. ^^ 그래서 C# 12부터 제공하는 기본 생성자는 record와 유사한 문법을 제공하는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PersonDTO p = new PersonDTO("John", 42); public <span style='color: blue; font-weight: bold'>class PersonDTO</span>(string name, int age); </pre> <br /> 특이한 것은, 인자로 제공하는 필드가 (record의 경우 public 속성으로 제공하는 것과는 달리) 아무런 역할도 없다는 것입니다. 그래서 단순히 위와 같이 정의하면 각각의 매개변수에 대해 이런 컴파일 경고가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > warning CS9113: Parameter 'name' is unread. warning CS9113: Parameter 'age' is unread. </pre> <br /> 왜 이런 경고가 발생하는지는 위의 코드를 역어셈블한 코드에서 알 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Runtime.CompilerServices; // Token: 0x02000007 RID: 7 public class PersonDTO { // Token: 0x06000008 RID: 8 RVA: 0x000020D2 File Offset: 0x000002D2 [NullableContext(1)] public PersonDTO(string name, int age) { } } </pre> <br /> 말 그대로 "기본 생성자(Primary Constructors)"를 정의해 주는 것일 뿐 아무런 역할도 하지 않는데요, 물론 이게 끝이라면 정말 쓸모없는 문법이었을 테지만, 다행히 전달된 name, age 매개변수를 내부 멤버에서 연동해 사용할 수 있기 때문에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class PersonDTO(string <span style='color: blue; font-weight: bold'>name</span>, int <span style='color: blue; font-weight: bold'>age</span>) { public string Name { get => <span style='color: blue; font-weight: bold'>name</span>; private set => Name = <span style='color: blue; font-weight: bold'>name</span>; } // 또는, public string Name { get; private set; } = <span style='color: blue; font-weight: bold'>name</span>; public int Age { get => <span style='color: blue; font-weight: bold'>age</span>; private set => Age = <span style='color: blue; font-weight: bold'>age</span>; } // 또는, public int Age { get; private set; } = <span style='color: blue; font-weight: bold'>age</span>; } </pre> <br /> 그나마 약간의 부가적인 코드를 줄여주는 효과를 갖긴 합니다. 즉, 이전에는 다음과 같이 작성하던 것에 불과한데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class Person { public string Name { get; private set; } public int Age { get; private set; } public Person(string name, int age) { this.Name = name; this.Age = age; } } /* 또는, 필드로도 가능. public class Person(string name, int age) { public string Name = name; public int Age = age; } */ </pre> <br /> 실제로 위의 코드는 기본 생성자를 역어셈블한 코드와 유사합니다. 언뜻 효과가 미비한 듯하지만 그래도 필드가 많아지면 꽤나 타이핑 수를 줄일 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 결국 내부적으로 연동하는 것은 별도의 코드로 작성한 멤버 필드 또는 속성이고, 그것이 외부로 드러나는 것이기 때문에 만약 구조체의 경우 with 문과 사용하고 싶다면 다음과 같이 속성의 set을 init으로 바꾸면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Person p = new Person("John", 42); Person p2 = p with { Age = 43 }; public <span style='color: blue; font-weight: bold'>struct Person</span>(string name, int age) { public string Name { get; <span style='color: blue; font-weight: bold'>init</span>; } = name; public int Age { get; <span style='color: blue; font-weight: bold'>init</span>; } = age; } </pre> <br /> 이 정도면, 대충 어떤 의미인지 아시겠죠? ^^ 한 가지 유의할 사항이 있다면, 기본 생성자를 정의한 클래스의 경우 다른 생성자를 정의하게 되면 반드시 기본 생성자를 경유해야 한다는 점입니다. 따라서 이전 예제에서 Age만 초기화하는 생성자를 다음과 같이 만들면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class PersonDTO(string name, int age) { public string Name { get; private set; } = name; public int Age { get; private set; } = age; public PersonDTO(int age) // 컴파일 오류: error CS8862: A constructor declared in a type with parameter list must have 'this' constructor initializer. { this.Name = ""; this.Age = age; } } </pre> <br /> 컴파일 오류가 발생합니다. 즉, 다음과 같이 기본 생성자와 반드시 연결해야만 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class PersonDTO(string name, int age) { // ...[생략]... <span style='color: blue; font-weight: bold'>public PersonDTO(int age) : this("", age) { }</span> } </pre> <br /> 이외에도, 자동 구현 속성의 이름은 내부적으로 string '<Name>k__BackingField', '<Age>k__BackingField'와 같은 식으로 필드를 생성하기 때문에 기본 생성자의 이름과 같은 필드를 정의하는 것은 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public struct Person(<span style='color: blue; font-weight: bold'>string name</span>, int age) { <span style='color: blue; font-weight: bold'>string name;</span> public string Name { get; init; } = name; // 기본 생성자의 name 매개변수와 연동 public int Age { get; init; } = age; // 기본 생성자의 age 매개변수와 연동 } </pre> <br /> 단지, 권장할 수는 없을 것입니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> "기본 생성자"라는 번역이 다소 애매한데요, 안 그래도 기존 클래스 또는 구조체의 경우 사용자가 정의하지 않아도 제공되는 생성자도,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 기본 생성자 ; <a target='tab' href='https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/language-specification/types#default-constructors'>https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/language-specification/types#default-constructors</a> </pre> <br /> "기본 생성자(Default constructor)"라고 하기 때문에 헷갈릴 수 있습니다.<br /> <br /> 어찌 보면 "주 생성자", "대표 생성자" 등이 어울릴 것 같은데 일단은 C# 12 문서에 나온 용어라서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 12의 새로운 기능 ; <a target='tab' href='https://learn.microsoft.com/ko-kr/dotnet/csharp/whats-new/csharp-12'>https://learn.microsoft.com/ko-kr/dotnet/csharp/whats-new/csharp-12</a> </pre> <br /> 그에 따라 "기본 생성자"라고 번역했습니다. 아마도 이것 때문인지 기존의 "기본 생성자(Default constructor)"는 "<a target='tab' href='https://learn.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#parameterless-constructors'>매개변수 없는 생성자</a>"라고 <a target='tab' href='https://learn.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors'>문서</a>에 나옵니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1841
(왼쪽의 숫자를 입력해야 합니다.)