Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 3개 있습니다.)
.NET Framework: 408. 자바와 닷넷의 제네릭 차이점 - 중간 언어 및 공변/반공변 처리
; https://www.sysnet.pe.kr/2/0/1581

.NET Framework: 743. C# 언어의 공변성과 반공변성
; https://www.sysnet.pe.kr/2/0/11513

닷넷: 2264. C# - 형식 인자로 인터페이스를 갖는 제네릭 타입으로의 형변환
; https://www.sysnet.pe.kr/2/0/13636




C# - 형식 인자로 인터페이스를 갖는 제네릭 타입으로의 형변환

짧은 제목으로 표현하기 좀 힘들군요. ^^; 예를 들어, 아래와 같이 인터페이스를 상속받는 타입의 경우,

public interface IData
{
}

public class DataImpl : IData { }

때로는 저 DataImpl 타입을 반환하는 제네릭 타입이 있을 것입니다.

static private Task<DataImpl> GetDataImplAsync()
{
    // Task.Run으로 해도 되지만, 테스트 프로젝트를 .NET Framework 4.0으로 맞추느라 Task.Factory를 사용
    Task<DataImpl> task = Task.Factory.StartNew<DataImpl>(() =>
    {
        DataImpl data = new DataImpl();
        return data;
    });

    return task;
}

문제는, 저런 경우 IData 인터페이스로 공통 처리할 수 있는 방법이 없습니다. 즉, 다음과 같이 인터페이스가 아닌, 구현 타입을 갖는 제네릭을 받아야 합니다. (이에 대해서는 전에 자세히 다룬 글이 있고, C# 4부터 인터페이스에 한해 공변을 지원하는 보완을 했습니다.)

// error CS0029: Cannot implicitly convert type 'System.Threading.Tasks.Task<DataImpl>' to 'System.Threading.Tasks.Task<IData>'
// Task<IData> data2 = GetDataImplAsync();

Task<DataImpl> data = GetDataImplAsync();

저런 제약 때문에 DataImpl 구현 클래스를 담은 어셈블리를 반드시 참조하거나, 아니면 Reflection을 써서 접근을 해야 합니다. 물론 이런 경우 Reflection보다는 dynamic을 쓰는 것이 더 좋은 선택입니다.

dynamic data = GetDataImplAsync();
IData iData = data.Result; // 인터페이스로 형변환




만약 dynamic을 쓰고 싶지 않다면 어떻게 해야 할까요? 어쩔 수 없이 그럴 때는 Reflection으로 접근해야 합니다. 그런데, 다시 한번 만약, ^^ Reflection 속도보다 빠르게 접근하고 싶다면 어떻게 해야 할까요? 이럴 때는 (역시 예전에 dynamic과 비교해 설명한) LCG를 사용하면 됩니다.

그래서 대충 이런 식으로 만들어 주면,

private static Dictionary<string, Func<object, object>> _taskResultFunc = new();

private static object GetDynamicResult(object objValue)
{
    Type type = objValue.GetType();

    if (_taskResultFunc.TryGetValue(type.FullName, out var resultFunc) == false)
    {
        PropertyInfo prop = type.GetProperty("Result");
        MethodInfo mi = prop.GetGetMethod();

        DynamicMethod dm = new DynamicMethod(
            Guid.NewGuid().ToString(),
            typeof(object), [typeof(object)], typeof(Program), false);

        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Isinst, type);
        il.Emit(OpCodes.Callvirt, mi);
        il.Emit(OpCodes.Ret);

        resultFunc = (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>));
        _taskResultFunc[type.FullName] = resultFunc;
    }

    return resultFunc(objValue);
}

이후 대상을 직접 참조하지 않고도 인터페이스 형변환만으로 다룰 수 있습니다.

IData result = GetDynamicResult(objData) as IData;

설령 이렇게 구현 클래스가 추가돼도,

public class AttrImpl : IData { }

static private Task<AttrImpl> GetAttrImplAsync()
{
    Task<AttrImpl> task = Task.Factory.StartNew<AttrImpl>(() =>
    {
        AttrImpl data = new AttrImpl();
        return data;
    });

    return task;
}

마찬가지의 방식으로 다룰 수 있습니다.

object objAttr = GetAttrImplAsync();
result = GetDynamicResult(objAttr) as IData;

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




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/17/2024]

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

비밀번호

댓글 작성자
 




... [121]  122  123  124  125  126  127  128  129  130  131  132  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
10899정성태2/17/201623373개발 환경 구성: 282. kernel32.dll, kernel32legacy.dll, api-ms-win-core-sysinfo-l1-2-0.dll [1]
10898정성태2/17/201621835.NET Framework: 547. PerformanceCounter의 InstanceName 지정 시 주의 사항파일 다운로드1
10897정성태2/17/201621226디버깅 기술: 76. windbg 분석 사례 - 닷넷 프로파일러의 GC 콜백 부하
10896정성태2/17/201622341오류 유형: 320. FATAL: 28000: no pg_hba.conf entry for host "fe80::1970:8120:695:a41e%12"
10895정성태2/17/201621124.NET Framework: 546. System.AppDomain으로부터 .NET Profiler의 AppDomainID 구하는 방법 [1]
10894정성태2/17/201621844오류 유형: 319. Visual Studio에서 찾기는 성공하지만 해당 소스 코드 정보가 보이지 않는 경우
10893정성태2/16/201620513.NET Framework: 545. 닷넷 - 특정 클래스가 로드되었는지 여부를 알 수 있을까? - 두 번째 이야기
10892정성태2/16/201621108오류 유형: 318. 탐색기에서 폴더 생성/삭제 시 몇 초 동안 멈추는 현상
10891정성태2/16/201624133VC++: 95. 내 CPU가 MPX/SGX를 지원할까요? [1]
10890정성태2/15/201624028.NET Framework: 544. C# 5의 Caller Info를 .NET 4.5 미만의 응용 프로그램에 적용하는 방법 [5]
10889정성태2/14/201620313.NET Framework: 543. C++의 inline asm 사용을 .NET으로 포팅하는 방법 - 두 번째 이야기파일 다운로드1
10888정성태2/14/201618645.NET Framework: 542. 닷넷 - 특정 클래스가 로드되었는지 여부를 알 수 있을까?
10887정성태2/3/201619332VC++: 94. MPX(Memory Protection Extensions) 테스트파일 다운로드1
10886정성태2/3/201620559개발 환경 구성: 281. Intel MPX Runtime Driver 수동 설치
10885정성태2/2/201620267오류 유형: 317. Sybase.Data.AseClient.AseException: The command has timed out.
10884정성태1/11/201621463개발 환경 구성: 280. 닷넷에서 SAP Adaptive Server Enterprise 데이터베이스 사용파일 다운로드1
10882정성태1/6/201620759Windows: 113. 윈도우의 2179, 26143, 47001 TCP 포트 사용 [1]
10881정성태1/3/201622184오류 유형: 316. 윈도우 10 - 바탕/돋음 체가 사라져 한글이 깨지는 현상 [2]
10880정성태12/16/201519898오류 유형: 315. 닷넷 프로파일러의 오류 코드 정보
10879정성태12/16/201521828오류 유형: 314. Error : DEP0700 : Registration of the app failed. error 0x80070005
10878정성태12/9/201524867디버깅 기술: 75. UWP(유니버설 윈도우 플랫폼) 앱에서 global::System.Diagnostics.Debugger.Break 예외 발생 시 대응 방법
10877정성태12/9/201529296VC++: 93. std::thread 사용 시 R6010 오류 [2]
10876정성태11/26/201525361.NET Framework: 541. SignedXml을 이용한 ds:Signature만드는 방법 [3]파일 다운로드1
10875정성태11/26/201530337개발 환경 구성: 279. signtool.exe의 다중 서명 기능 [2]
10874정성태11/26/201526349개발 환경 구성: 278. 인증서와 인증서를 이용한 코드 사인의 해시 구분
10873정성태11/25/201525462.NET Framework: 540. C# - 부동 소수 계산 왜 이렇게 나오죠? (2) [3]파일 다운로드1
... [121]  122  123  124  125  126  127  128  129  130  131  132  133  134  135  ...