성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능 (DIM for Static Members)</h1> <p> <strong>(2022-07-12 업데이트) 이 기능은 C# 11에 포함될 예정이고, .NET 7 환경을 필요로 합니다.</strong><br /> <br /> <hr style='width: 50%' /><br /> <br /> 앞서 C# 8부터 기본 인터페이스 메서드가 추가되었습니다. 즉, 다음과 같이 인터페이스의 구현이 가능해졌고,<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> IMessage { public static int None = 0; // 정적 메서드/필드 가능 public static int Any { get; } // 속성(및 이벤트)도 결국 메서드이므로. public void WriteLog(string text) => Console.WriteLine(text); // 인스턴스 메서드 가능 } </pre> <br /> 이를 지원하는 런타임은 (.NET Framework은 안 되고) .NET Core 3.0부터입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그동안, 인터페이스는 내부에 명세한 "인스턴스 멤버"에 대해서는 하위 클래스에서 그것을 구현하도록 강제하는 것이 가능했습니다. 반면, 정적 메서드에 대해서는 이것이 불가능했는데요, 실제로 위의 코드에서 정적 속성 Any를 IMessage를 구현한 하위 클래스에서 구현 코드를 제공하도록 강제할 방법이 없습니다.<br /> <br /> 바로 이것을 지원하기 위해 새롭게 다음의 문법이 추가되었고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Static abstract members in interfaces ; <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/static-abstracts-in-interfaces.md'>https://github.com/dotnet/csharplang/blob/main/proposals/static-abstracts-in-interfaces.md</a> </pre> <br /> 따라서, 정적 메서드를 하위 클래스에서 구현하도록 abstract 예약어를 이용해 다음과 같이 강제할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public interface IMessage { public static int None = 0; // C# 10 이전에는 컴파일 오류 발생 // error CS0112: A static member 'IMessage<T>.Any' cannot be marked as override, virtual, or abstract <span style='color: blue; font-weight: bold'>public static abstract int Any { get; }</span> <span style='color: blue; font-weight: bold'>static abstract void All();</span> } public class <span style='color: blue; font-weight: bold'>Message : IMessage</span> { <span style='color: blue; font-weight: bold'>public static int Any => 5; public static void All() { Console.WriteLine("All called"); }</span> public Message() { } } </pre> <br /> 즉, 이제는 인스턴스 메서드뿐만 아니라 정적 메서드까지도 하위 클래스에서의 구현을 강제할 수 있게 되었지만, 여기서 중요한 것은 그렇다고 해서 클래스 수준의 "다형성"이 제공되는 것은 아니라는 점입니다.<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;' > // 컴파일 오류 발생 // error CS8926: A static abstract interface member can be accessed only on a type parameter. // Console.WriteLine(<span style='color: blue; font-weight: bold'>IMessage.Any</span>); Console.WriteLine(<span style='color: blue; font-weight: bold'>Message.Any</span>); </pre> <br /> <hr style='width: 50%' /><br /> <br /> 사실, default interface method 정도는 자바로부터의,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 자바 코드 interface ITest { public static int Version = 5; public default void Log() { System.out.println("Log"); } } </pre> <br /> 이식성을 높이기 위함이라는 정도로 이해해 줄 수 있습니다. 그런데, 추상 정적 메서드를 interface에 넣어 도대체 어디다 쓰려고 그러는 것일까요? ^^ 대개의 경우 이런 특이한 기능은, 마이크로소프트가 필요했다고 보면 됩니다. 실제로, 이것은 이전에 소개한 INumber<T>에서 사용하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET 6 Preview 7에 추가된 숫자 형식에 대한 제네릭 연산 지원 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12785'>https://www.sysnet.pe.kr/2/0/12785</a> </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 static T 합계<T>(this IEnumerable<T> arg) <span style='color: blue; font-weight: bold'>where T : INumber<T></span> { T sum = <span style='color: blue; font-weight: bold'>T.Zero</span>; foreach (T item in arg) { sum = sum + item; } return sum; } public static TResult 산술평균<T, TResult>(this IEnumerable<T> arg) <span style='color: blue; font-weight: bold'>where T : INumber<T></span> where TResult : INumber<TResult> { return <span style='color: blue; font-weight: bold'>TResult.Create</span>(arg.합계()) / <span style='color: blue; font-weight: bold'>TResult.Create</span>(arg.Count()); } </pre> <br /> 바로 저 코드들이 INumber<T>의 abstract static 메서드였던 것입니다. 저렇게 보니, 정말 유용한 기능이긴 합니다. ^^<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1857&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<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;' > interface IMy { static abstract void M(); static abstract IMy P { get; set; } static abstract event Action E; static abstract IMy operator +(IMy l, IMy r); static abstract bool operator ==(IMy l, IMy r); static abstract bool operator !=(IMy l, IMy r); // error CS0552: 'IMy.implicit operator IMy(string)': user-defined conversions to or from an interface are not allowed // static abstract implicit operator IMy(string s); // error CS0552: 'IMy.implicit operator string(IMy)': user-defined conversions to or from an interface are not allowed // static abstract implicit operator string(IMy s); static abstract bool operator !(IMy l); } interface IYour<T> { static abstract void M(); static abstract T P { get; set; } static abstract event Action E; // error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract T operator +(T l, T r); // error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator ==(T l, T r); // error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator !=(T l, T r); // error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract implicit operator T(string s); // error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator string(T t); // error CS8921: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator !(T l); } interface ITheir<T> where T: ITheir<T> { static abstract void M(); static abstract T P { get; set; } static abstract T operator +(T l, T r); static abstract implicit operator T(string s); static abstract explicit operator string(T t); static abstract bool operator !(T l); } public class TheirClass : ITheir<TheirClass> { public static TheirClass P { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public static void M() { } public static TheirClass operator +(TheirClass l, TheirClass r) => throw new NotImplementedException(); public static implicit operator TheirClass(string s) => throw new NotImplementedException(); public static explicit operator string(TheirClass t) => "type"; public static bool operator !(TheirClass l) => true; } class YourClass<T> where T : ITheir<T> { object Method() { return T.P; } } </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>) ; https://www.sysnet.pe.kr/2/0/12814 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>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13123'>https://www.sysnet.pe.kr/2/0/13123</a> 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>
첨부파일
스팸 방지용 인증 번호
1891
(왼쪽의 숫자를 입력해야 합니다.)