성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
[정성태] 저도 해 본 것은 아니지만 어차피 in-process로 pyth...
[정성태] Full Text Search With ElasticSearch...
[정성태] When I define a window class with n...
[정성태] What is the process by which the cu...
[정성태] Why doesn't the clock in the taskba...
[정성태] 오히려 그 글을 읽지 않았다고 생각하시는 것이 편합니다. (기억...
[GM B] 늦었지만 답변감사드립니다. 제가 좀 바빠서 이제서야... ...
[정성태] 관련해서는 ContextMenu의 XAML template을 분...
글쓰기
제목
이름
암호
전자우편
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# 11 - 멤버(속성/필드)에 지정할 수 있는 required 예약어 추가</h1> <p> 개체 생성 시에 반드시 초기화를 강제할 수 있는 옵션이 C# 11부터 required라는 예약어를 통해 제공됩니다.<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 <span style='color: blue; font-weight: bold'>required</span> int Age; public <span style='color: blue; font-weight: bold'>required</span> string FirstName { get; init; } public string MiddleName { get; init; } = ""; public <span style='color: blue; font-weight: bold'>required</span> string LastName { get; init; } } public class Employee { public <span style='color: blue; font-weight: bold'>required</span> int Age; public string Name { get; init; } public Employee(string name) { this.Name = name; } } </pre> <br /> 위와 같이 정의한 타입은 이제 다음과 같은 식으로 required 멤버 초기화를 빼먹지 말고 "<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers'>개체 초기화 구문</a>"과 함께 new를 해야 합니다.<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 { <span style='color: blue; font-weight: bold'>Age = 62</span>, FirstName = "Anders", LastName = "Hejlsberg" }; Employee e = new Employee("Anders") <span style='color: blue; font-weight: bold'>{ Age = 62 }</span>; // 하나라도 빼먹으면, 컴파일 에러: error CS9035: Required member 'Person.Age' must be set in the object initializer or attribute constructor. Person p1 = new Person { FirstName = "Anders", LastName = "Hejlsberg" }; </pre> <br /> 개체 초기화에 사용하려면 당연히 외부에서 접근이 가능해야 하므로 required 멤버는 그것을 소유한 class의 접근성을 만족해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>internal</span> class Employee { <span style='color: blue; font-weight: bold'>internal required</span> int Age; // .. } </pre> <br /> 즉, class는 public인데, required 멤버가 internal 이하의 접근성을 가진다면 이런 식의 오류 메시지가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > error CS9032: Required member 'Employee.Age' cannot be less visible or have a setter less visible than the containing type 'Employee'. </pre> <br /> 재미있는 것은, required 필드가 개체 초기화 구문을 통해서만 값을 설정해야 유효하다는 점입니다. 가령, Employee 타입의 생성자에 required 필드 값을 초기화한다면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 컴파일 오류: error CS9035: Required member 'Employee.Age' must be set in the object initializer or attribute constructor. Employee e = <span style='color: blue; font-weight: bold'>new Employee("Anders");</span> Employee e = <span style='color: blue; font-weight: bold'>new Employee("Anders", 62);</span> public class Employee { public <span style='color: blue; font-weight: bold'>required int Age;</span> public string Name { get; init; } public Employee(string name) { this.Name = name; <span style='color: blue; font-weight: bold'>this.Age = 0;</span> } /* 또는, 생성자에 값을 전달하도록 제공해도, */ public Employee(string name, int age) { this.Name = name; <span style='color: blue; font-weight: bold'>this.Age = age;</span> } } </pre> <br /> 그래도 컴파일 오류가 발생합니다. 이건 좀 그렇죠? required에 해당하는 멤버를 생성자에서 초기화했으면 C# 컴파일러가 인식해서 넘어가도 좋을 듯한데, 아쉽게도 그걸 허용하지 않는 겁니다. 이럴 때 개발자가 직접 해당 생성자에서는 required 멤버를 모두 초기화한 것으로 가정하라고 컴파일러에게 SetsRequiredMembers라는 특성을 지정해 통과하는 방법이 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 정상적으로 컴파일 <span style='color: blue; font-weight: bold'>Employee e = new Employee("Anders");</span> public class Employee { // ... Age, Name 멤버 ... <span style='color: blue; font-weight: bold'>[SetsRequiredMembers]</span> public Employee(string name) { this.Name = name; this.Age = 0; } } </pre> <br /> 또한, SetsRequiredMembers 특성이 부여된 생성자는 required 멤버를 꼭 초기화하지 않아도 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 그래도 정상적으로 컴파일 Employee e = new Employee("Anders"); public class Employee { // ... Age, Name 멤버 ... <span style='color: blue; font-weight: bold'>[SetsRequiredMembers]</span> public Employee(string name) { this.Name = name; } } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그럼 이런 특징이 클래스 상속으로 넘어오면 어떻게 될까요? Employee를 이렇게 정의한 경우,<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 Employee { <span style='color: blue; font-weight: bold'>public required</span> int Age; public string Name { get; init; } public Employee(string name) { this.Name = name; } } </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 Salesman : Employee { public Salesman(string name) : base(name) { } } </pre> <br /> 어차피 base 클래스의 required 멤버가 public이므로 하위 클래스를 사용할 때도 개체 초기화 구문을 사용해 정의하는 것이 가능하기 때문입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Salesman s = new Salesman("Mark") { Age = 30 }; </pre> <br /> 단지, 부모 클래스의 생성자를 연계하는 경우라면 SetsRequiredMembers도 상속 클래스의 생성자에서 지정해야 한다는 정도만 알아두시면 되겠습니다.<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 Salesman : Employee { // 연동하려는 부모 클래스의 생성자가 SetsRequiredMembers 특성을 지정했으므로! // 만약 자식 클래스에서 지정하지 않으면 "CS9039 This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute." 컴파일 오류 <span style='color: blue; font-weight: bold'>[SetsRequiredMembers]</span> public Salesman(string name) : base(name) { } } public class Employee { public required int Age; public string Name { get; init; } <span style='color: blue; font-weight: bold'>[SetsRequiredMembers]</span> public Employee(string name) { this.Name = name; } } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 기타 제약이라면, required 멤버는 class, struct, record에서만 허용되고 interface에는 정의할 수 없습니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public <span style='color: blue; font-weight: bold'>interface</span> IEmployee { // error CS0106: The modifier 'required' is not valid for this item <span style='color: blue; font-weight: bold'>required</span> int Age { get; } } </pre> <br /> 또한, 다음의 예약어가 적용된 멤버는 required를 조합해 적용할 수 없습니다.<br /> <br /> <ul> <li>fixed</li> <li>ref readonly</li> <li>ref</li> <li>const</li> <li>static</li> </ul> <br /> 마지막으로, (굳이 언급해야 할 필요가 있을까 싶지만) property 정의 구문과 유사한 indexer의 경우에도 그 특성상 required를 적용할 수 없습니다.<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 Number { // 컴파일 오류: error CS0106: The modifier 'required' is not valid for this item public <span style='color: blue; font-weight: bold'>required</span> int this[int i] { get { return i; } } } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, C# 컴파일러는 required 멤버를 컴파일 시 RequiredMemberAttribute 특성을 함께 추가해 컴파일합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 원본 소스 코드 public <span style='color: blue; font-weight: bold'>required</span> int Age; // 컴파일 후 <span style='color: blue; font-weight: bold'>[RequiredMember]</span> public int Age; </pre> <br /> 저렇게 보면, 개발자가 "required" 대신 직접 [RequiredMember] 특성을 부여해도 될 것 같은데요, 하지만 실제로 해보면 ^^ C# 컴파일러가 required를 사용하라며 오류를 냅니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 컴파일 오류: error CS9033: Do not use 'System.Runtime.CompilerServices.RequiredMemberAttribute'. Use the 'required' keyword on required fields and properties instead. [RequiredMember] public int Age; </pre> <br /> 어쨌든, required로 인해 .NET 7 BCL에는 2개의 타입(RequiredMemberAttribute, SetsRequiredMembersAttribute)이 추가되는데요, 만약 .NET 6 이하를 대상으로 하는 프로젝트에서 사용하려고 한다면 다음과 같이 직접 타입 정의를 추가하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // C# 6.0 프로젝트 using System.Diagnostics.CodeAnalysis; Employee e = new Employee("Anders"); Console.WriteLine(e); public class Employee { public required int Age; public string Name { get; init; } [SetsRequiredMembers] public Employee(string name) { this.Name = name; } } /* 아래의 타입을 정의하지 않으면 컴파일 오류 발생 error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RequiredMemberAttribute..ctor' error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute..ctor' error CS0246: The type or namespace name 'SetsRequiredMembersAttribute' could not be found (are you missing a using directive or an assembly reference?) error CS0246: The type or namespace name 'SetsRequiredMembers' could not be found (are you missing a using directive or an assembly reference?) */ <span style='color: blue; font-weight: bold'> #if !NET7_0_OR_GREATER namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public sealed class RequiredMemberAttribute : Attribute { public RequiredMemberAttribute() { } } [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] public sealed class CompilerFeatureRequiredAttribute : Attribute { public string FeatureName { get; } public CompilerFeatureRequiredAttribute(string featureName) { this.FeatureName = featureName; } } } namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] public sealed class SetsRequiredMembersAttribute : Attribute { public SetsRequiredMembersAttribute() { } } } #endif </span> </pre> <br /> <hr style='width: 50%' /><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4436'>Static Abstract Members In Interfaces C# 10 Preview</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12814'>https://www.sysnet.pe.kr/2/0/12814</a> C# 11 - 제네릭 타입의 특성 적용 (<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-attributes'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/124'>Generic attributes</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12839'>https://www.sysnet.pe.kr/2/0/12839</a> C# 11 - 사용자 정의 checked 연산자 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/checked-user-defined-operators.md'>Checked user-defined operators</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13099'>https://www.sysnet.pe.kr/2/0/13099</a> C# 11 - shift 연산자 재정의에 대한 제약 완화 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4666'>Relaxing Shift Operator</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13100'>https://www.sysnet.pe.kr/2/0/13100</a> C# 11 - IntPtr/UIntPtr과 nint/unint의 통합 (<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#numeric-intptr-and-uintptr'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/numeric-intptr.md'>Numeric IntPtr</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13111'>https://www.sysnet.pe.kr/2/0/13111</a> C# 11 - 새로운 연산자 ">>>" (Unsigned Right Shift) (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4682'>Unsigned right shift operator</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13110'>https://www.sysnet.pe.kr/2/0/13110</a> C# 11 - 원시 문자열 리터럴 (<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#raw-string-literals'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/raw-string-literal.md'>raw string literals</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13085'>https://www.sysnet.pe.kr/2/0/13085</a> C# 11 - 문자열 보간 개선 2가지 (<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#newlines-in-string-interpolations'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4935'>Allow new-lines in all interpolations</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13086'>https://www.sysnet.pe.kr/2/0/13086</a> C# 11 - 목록 패턴 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/list-patterns.md'>List patterns</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13112'>https://www.sysnet.pe.kr/2/0/13112</a> C# 11 - Span 타입에 대한 패턴 매칭 (<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#pattern-match-spanchar-or-readonlyspanchar-on-a-constant-string'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/1881'>Pattern matching on ReadOnlySpan<char></a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13113'>https://www.sysnet.pe.kr/2/0/13113</a> C# 11 - Utf8 문자열 리터럴 지원 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/utf8-string-literals.md'>Utf8 Strings Literals</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13096'>https://www.sysnet.pe.kr/2/0/13096</a> C# 11 - ref struct에 ref 필드를 허용 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md'>ref fields</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13015'>https://www.sysnet.pe.kr/2/0/13015</a> C# 11 - 파일 범위 내에서 유효한 타입 정의 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/issues/6011'>File-local types</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13117'>https://www.sysnet.pe.kr/2/0/13117</a> C# 11 - 메서드 매개 변수에 대한 nameof 지원 (<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#extended-nameof-scope'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/373'>nameof(parameter)</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13122'>https://www.sysnet.pe.kr/2/0/13122</a> C# 11 - 멤버(속성/필드)에 지정할 수 있는 required 예약어 추가 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/required-members.md'>Required members</a>) ; https://www.sysnet.pe.kr/2/0/13123 C# 11 - 구조체 필드의 자동 초기화 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/auto-default-structs.md'>auto-default structs</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13125'>https://www.sysnet.pe.kr/2/0/13125</a> C# 11 - 정적 메서드에 대한 delegate 처리 시 cache 적용 (공식 문서, <a target='tab' href='https://github.com/dotnet/roslyn/issues/5835'>Cache delegates for static method group</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13126'>https://www.sysnet.pe.kr/2/0/13126</a> Language Feature Status ; <a target='tab' href='https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md'>https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md</a> </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1762
(왼쪽의 숫자를 입력해야 합니다.)