Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

C# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능 (DIM for Static Members)

(2022-07-12 업데이트) 이 기능은 C# 11에 포함될 예정이고, .NET 7 환경을 필요로 합니다.




앞서 C# 8부터 기본 인터페이스 메서드가 추가되었습니다. 즉, 다음과 같이 인터페이스의 구현이 가능해졌고,

public interface IMessage
{
    public static int None = 0; // 정적 메서드/필드 가능

    public static int Any { get; } // 속성(및 이벤트)도 결국 메서드이므로.

    public void WriteLog(string text) => Console.WriteLine(text); // 인스턴스 메서드 가능
}

이를 지원하는 런타임은 (.NET Framework은 안 되고) .NET Core 3.0부터입니다.




그동안, 인터페이스는 내부에 명세한 "인스턴스 멤버"에 대해서는 하위 클래스에서 그것을 구현하도록 강제하는 것이 가능했습니다. 반면, 정적 메서드에 대해서는 이것이 불가능했는데요, 실제로 위의 코드에서 정적 속성 Any를 IMessage를 구현한 하위 클래스에서 구현 코드를 제공하도록 강제할 방법이 없습니다.

바로 이것을 지원하기 위해 새롭게 다음의 문법이 추가되었고,

Static abstract members in interfaces
; https://github.com/dotnet/csharplang/blob/main/proposals/static-abstracts-in-interfaces.md

따라서, 정적 메서드를 하위 클래스에서 구현하도록 abstract 예약어를 이용해 다음과 같이 강제할 수 있습니다.

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
    public static abstract int Any { get; }

    static abstract void All();
}

public class Message : IMessage
{
    public static int Any => 5;

    public static void All()
    {
        Console.WriteLine("All called");
    }

    public Message()
    {
    }
}

즉, 이제는 인스턴스 메서드뿐만 아니라 정적 메서드까지도 하위 클래스에서의 구현을 강제할 수 있게 되었지만, 여기서 중요한 것은 그렇다고 해서 클래스 수준의 "다형성"이 제공되는 것은 아니라는 점입니다.

따라서, 사용 시에는 단순히 해당 정적 멤버가 구현된 클래스 이름을 특정해서 사용해야 합니다.

// 컴파일 오류 발생
// error CS8926: A static abstract interface member can be accessed only on a type parameter.
// Console.WriteLine(IMessage.Any);

Console.WriteLine(Message.Any);




사실, default interface method 정도는 자바로부터의,

// 자바 코드
interface ITest {
    public static int Version = 5;

    public default void Log() {
        System.out.println("Log");
    }
}

이식성을 높이기 위함이라는 정도로 이해해 줄 수 있습니다. 그런데, 추상 정적 메서드를 interface에 넣어 도대체 어디다 쓰려고 그러는 것일까요? ^^ 대개의 경우 이런 특이한 기능은, 마이크로소프트가 필요했다고 보면 됩니다. 실제로, 이것은 이전에 소개한 INumber<T>에서 사용하고 있습니다.

.NET 6 Preview 7에 추가된 숫자 형식에 대한 제네릭 연산 지원
; https://www.sysnet.pe.kr/2/0/12785

위의 글에 실어 놓았던 코드를 다시 보면,

public static T 합계<T>(this IEnumerable<T> arg) where T : INumber<T>
{
    T sum = T.Zero;

    foreach (T item in arg)
    {
        sum = sum + item;
    }

    return sum;
}

public static TResult 산술평균<T, TResult>(this IEnumerable<T> arg) where T : INumber<T>
    where TResult : INumber<TResult>
{
    return TResult.Create(arg.합계()) / TResult.Create(arg.Count());
}

바로 저 코드들이 INumber<T>의 abstract static 메서드였던 것입니다. 저렇게 보니, 정말 유용한 기능이긴 합니다. ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




아래는 문서에 나온 몇 가지 메서드 유형을 예제로 나열한 것입니다.

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;
    }
}




C# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능(공식 문서, Static Abstract Members In Interfaces C# 10 Preview)
; https://www.sysnet.pe.kr/2/0/12814

C# 11 - 제네릭 타입의 특성 적용 (공식 문서, Generic attributes)
; https://www.sysnet.pe.kr/2/0/12839

C# 11 - 원시 문자열 리터럴(공식 문서, raw string literals)
; https://www.sysnet.pe.kr/2/0/13085

C# 11 - 문자열 보간 개선 2가지(공식 문서, Allow new-lines in all interpolations)
; https://www.sysnet.pe.kr/2/0/13086

C# 11 - Utf8 문자열 리터럴 지원(공식 문서, Utf8 Strings Literals)
; https://www.sysnet.pe.kr/2/0/13096

C# 11 - ref 필드(공식 문서, ref fields)
; https://www.sysnet.pe.kr/2/0/13015

Language Feature Status
; https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/31/2022]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  9  [10]  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
12891정성태12/23/2021992스크립트: 38. 파이썬 - uwsgi의 --master 옵션
12890정성태12/23/20211084VC++: 152. Golang - (문자가 아닌) 바이트 위치를 반환하는 strings.IndexRune 함수
12889정성태12/22/20211596.NET Framework: 1123. C# - (SharpDX + DXGI) 화면 캡처한 이미지를 빠르게 JPG로 변환하는 방법파일 다운로드1
12888정성태12/21/20211363.NET Framework: 1122. C# - ImageCodecInfo 사용 시 System.Drawing.Image와 System.Drawing.Bitmap에 따른 Save 성능 차이파일 다운로드1
12887정성태12/21/20211494오류 유형: 777. OpenCVSharp4를 사용한 프로그램 실행 시 "The type initializer for 'OpenCvSharp.Internal.NativeMethods' threw an exception." 예외 발생
12886정성태12/20/20211176스크립트: 37. 파이썬 - uwsgi의 --enable-threads 옵션
12885정성태12/20/2021950오류 유형: 776. uwsgi-plugin-python3 환경에서 MySQLdb 사용 환경
12884정성태12/20/2021958개발 환경 구성: 620. Windows 10+에서 WMI root/Microsoft/Windows/WindowsUpdate 네임스페이스 제거
12883정성태12/19/20211260오류 유형: 775. uwsgi-plugin-python3 환경에서 "ModuleNotFoundError: No module named 'django'" 오류 발생
12882정성태12/18/2021999개발 환경 구성: 619. Windows Server에서 WSL을 위한 리눅스 배포본을 설치하는 방법
12881정성태12/17/20211231개발 환경 구성: 618. WSL Ubuntu 20.04에서 파이썬을 위한 uwsgi 설치 방법 (2)
12880정성태12/16/20211021VS.NET IDE: 170. Visual Studio에서 .NET Core/5+ 역어셈블 소스코드 확인하는 방법
12879정성태12/16/20214357오류 유형: 774. Windows Server 2022 + docker desktop 설치 시 WSL 2로 선택한 경우 "Failed to deploy distro docker-desktop to ..." 오류 발생
12878정성태12/15/20211191개발 환경 구성: 617. 윈도우 WSL 환경에서 같은 종류의 리눅스를 다중으로 설치하는 방법
12877정성태12/15/2021992스크립트: 36. 파이썬 - pymysql 기본 예제 코드
12876정성태12/14/20211035개발 환경 구성: 616. Custom Sources를 이용한 Azure Monitor Metric 만들기
12875정성태12/13/20211037스크립트: 35. python - time.sleep(...) 호출 시 hang이 걸리는 듯한 문제
12874정성태12/13/2021861오류 유형: 773. shell script 실행 시 "$'\r': command not found" 오류
12873정성태12/12/20211285오류 유형: 772. 리눅스 - PATH에 등록했는데도 "command not found"가 나온다면?
12872정성태12/12/20211085개발 환경 구성: 615. GoLang과 Python 빌드가 모두 가능한 docker 이미지 만들기
12871정성태12/12/20212004오류 유형: 771. docker: Error response from daemon: OCI runtime create failed
12870정성태12/9/2021976개발 환경 구성: 614. 파이썬 - PyPI 패키지 만들기 (4) package_data 옵션
12869정성태12/8/20211193개발 환경 구성: 613. git clone 실행 시 fingerprint 묻는 단계를 생략하는 방법
12868정성태12/7/2021906오류 유형: 770. twine 업로드 시 "HTTPError: 400 Bad Request ..." 오류
12867정성태12/7/2021998개발 환경 구성: 612. 파이썬 - PyPI 패키지 만들기 (3) entry_points 옵션
12866정성태12/7/20214238오류 유형: 769. "docker build ..." 시 "failed to solve with frontend dockerfile.v0: failed to read dockerfile ..." 오류
1  2  3  4  5  6  7  8  9  [10]  11  12  13  14  15  ...