Microsoft MVP성태의 닷넷 이야기
닷넷: 2374. C# - dynamic과 "Explicit Interface Implementation"의 문제 [링크 복사], [링크+제목 복사],
조회: 661
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

C# - dynamic과 "Explicit Interface Implementation"의 문제

지난 글을 다루면서,

C# - dynamic 예약어 사용 시 런타임에 "Microsoft.CSharp.RuntimeBinder.RuntimeBinderException" 예외가 발생하는 경우
; https://www.sysnet.pe.kr/2/0/14032

RuntimeBinderException 예외에 대해 검색해 보면 이런 글이 나옵니다.

Gotchas in dynamic typing
; https://csharpindepth.com/articles/DynamicGotchas

위의 글에서 첫 번째 항목이 "Explicit interface implementation"과 dynamic 호출의 문제를 다루고 있는데요, 이에 대해 간략하게 정리해 보겠습니다. ^^




우선, "Explicit interface implementation"은 인터페이스의 멤버를 명시적으로 구현한 경우를 일컫습니다. 관련해서 아래의 공식 문서에서 잘 설명하고 있는데요,

Explicit Interface Implementation
; https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation

문서에 포함된 코드를 보면,

public interface IControl
{
    void Paint();
}

public interface ISurface
{
    void Paint();
}

public class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method.
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

동일한 signature를 가진 Paint 메서드를 2개의 인터페이스에서 선언하고 있고, 그것을 SampleClass에서 구현하고 있습니다. 그래서 다음과 같이 사용할 수 있는데요,

internal class Program
{
    static void Main(string[] args)
    {
        IControl inst1 = new SampleClass();
        inst1.Paint();

        ISurface inst2 = new SampleClass();
        inst2.Paint();
    }
}

어떤 경우에는 저게 의도한 구현일 수 있지만, 또 다른 경우에는 인터페이스마다 구현을 달리하고 싶을 수도 있습니다. 즉, 이렇게 나누고 싶은 경우인데요,

public class SampleClass : IControl, ISurface
{
    void IControl.Paint()
    {
        System.Console.WriteLine("IControl.Paint");
    }

    void ISurface.Paint()
    {
        System.Console.WriteLine("ISurface.Paint");
    }
}

바로 저렇게 (public 접근 제한자를 생략하고) "[Interface].[메서드명]" 형태로 구현하는 것을 두고 "Explicit interface implementation"이라고 부릅니다. 이렇게 정의한 메서드의 대표적인 특징은, 반드시 해당 인터페이스 타입으로 캐스팅한 후에만 호출할 수 있다는 점입니다.

SampleClass inst3 = new SampleClass();
inst3.Paint(); // 컴파일 오류: "Explicit interface implementation" 메서드는 직접 호출할 수 없습니다.

IControl control = inst3 as IControl;
control.Paint(); // 올바른 호출: IControl.Paint 메서드가 호출됩니다.

ISurface surface = inst3 as ISurface;
surface.Paint(); // 올바른 호출: ISurface.Paint 메서드가 호출됩니다.




이러한 명시적 인터페이스 구현이 dynamic 호출과 연관이 되는데요, 즉 다음과 같이 dynamic으로 변환한 인스턴스에 대해서는 "Explicit interface implementation" 메서드를 호출할 수 없다는 제약이 있습니다.

dynamic inst = new SampleClass();
inst.Paint(); // 예외 발생
              // Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'ConsoleApp3.SampleClass' does not contain a definition for 'Paint'

이에 대해 "Gotchas in dynamic typing" 글의 재현 코드에서는 ICollection이 구현된 System.Array 타입으로도 설명하고 있는데요,

System.Array arr = new int[] { 1, 2, 3 };
ICollection<int> coll = arr as ICollection<int>;
Console.WriteLine(coll.Count); // 출력 결과: 3

dynamic dnArr = arr;
Console.WriteLine(dnArr.Count); // 예외 발생
                                // Unhandled exception. Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.Array' does not contain a definition for 'Count'

실제로 System.Array의 소스 코드를 살펴보면,

// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Array.cs#L1011

[Serializable]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public abstract partial class Array : ICloneable, IList, IStructuralComparable, IStructuralEquatable
{
    internal Array() { }
    // ...[생략]...

    // Number of elements in the Array.
    int ICollection.Count => Length;

    // ...[생략]...
}

명시적인 인터페이스 구현을 확인할 수 있습니다. 참고로, "Gotchas in dynamic typing" 글에서는 이 사례가 C# 타입 시스템과 동적 형식 체계 간의 대표적인 "impedance mismatch"라고 언급하고 있습니다.

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




이 외에도 "Gotchas in dynamic typing" 글에서는 "Overloading ambiguity", "Compound assignment", "Anonymous types", "Generics" 및 WCF와 관련된 dynamic 문제들을 다루고 있으니 가볍게 살펴보시면 좋을 것 같습니다. ^^




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 10/22/2025]

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

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  [37]  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
13129정성태9/27/202219730.NET Framework: 2050. .NET Core를 IIS에서 호스팅하는 경우 .NET Framework CLR이 함께 로드되는 환경
13128정성태9/23/202223154C/C++: 158. Visual C++ - IDL 구문 중 "unsigned long"을 인식하지 못하는 #import [1]파일 다운로드1
13127정성태9/22/202220578Windows: 210. WSL에 systemd 도입
13126정성태9/15/202220837.NET Framework: 2049. C# 11 - 정적 메서드에 대한 delegate 처리 시 cache 적용
13125정성태9/14/202221341.NET Framework: 2048. C# 11 - 구조체 필드의 자동 초기화(auto-default structs)
13124정성태9/13/202221677.NET Framework: 2047. Golang, Python, C#에서의 CRC32 사용
13123정성태9/8/202221780.NET Framework: 2046. C# 11 - 멤버(속성/필드)에 지정할 수 있는 required 예약어 추가
13122정성태8/26/202222785.NET Framework: 2045. C# 11 - 메서드 매개 변수에 대한 nameof 지원
13121정성태8/23/202216666C/C++: 157. Golang - 구조체의 slice 필드를 Reflection을 이용해 변경하는 방법
13120정성태8/19/202222143Windows: 209. Windows NT Service에서 UI를 다루는 방법 [3]
13119정성태8/18/202222024.NET Framework: 2044. .NET Core/5+ 프로젝트에서 참조 DLL이 보관된 공통 디렉터리를 지정하는 방법
13118정성태8/18/202217534.NET Framework: 2043. WPF Color의 기본 색 영역은 (sRGB가 아닌) scRGB [2]
13117정성태8/17/202222418.NET Framework: 2042. C# 11 - 파일 범위 내에서 유효한 타입 정의 (File-local types)파일 다운로드1
13116정성태8/4/202222626.NET Framework: 2041. C# - Socket.Close 시 Socket.Receive 메서드에서 예외가 발생하는 문제파일 다운로드1
13115정성태8/3/202223541.NET Framework: 2040. C# - ValueTask와 Task의 성능 비교 [1]파일 다운로드1
13114정성태8/2/202223738.NET Framework: 2039. C# - Task와 비교해 본 ValueTask 사용법파일 다운로드1
13113정성태7/31/202223314.NET Framework: 2038. C# 11 - Span 타입에 대한 패턴 매칭 (Pattern matching on ReadOnlySpan<char>)
13112정성태7/30/202224911.NET Framework: 2037. C# 11 - 목록 패턴(List patterns) [1]파일 다운로드1
13111정성태7/29/202224327.NET Framework: 2036. C# 11 - IntPtr/UIntPtr과 nint/nuint의 통합파일 다운로드1
13110정성태7/27/202222213.NET Framework: 2035. C# 11 - 새로운 연산자 ">>>" (Unsigned Right Shift)파일 다운로드1
13109정성태7/27/202224316VS.NET IDE: 177. 비주얼 스튜디오 2022를 이용한 (소스 코드가 없는) 닷넷 모듈 디버깅 - "외부 원본(External Sources)" [1]
13108정성태7/26/202222217Linux: 53. container에 실행 중인 Golang 프로세스를 디버깅하는 방법 [1]
13107정성태7/25/202224968Linux: 52. Debian/Ubuntu 계열의 docker container에서 자주 설치하게 되는 명령어 [1]
13106정성태7/24/202219042오류 유형: 819. 닷넷 6 프로젝트의 "Conditional compilation symbols" 기본값 오류
13105정성태7/23/202223812.NET Framework: 2034. .NET Core/5+ 환경에서 (프로젝트가 아닌) C# 코드 파일을 입력으로 컴파일하는 방법 - 두 번째 이야기 [1]
13104정성태7/23/202227553Linux: 51. WSL - init에서 systemd로 전환하는 방법
... 31  32  33  34  35  36  [37]  38  39  40  41  42  43  44  45  ...