Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)

override 메서드가 정의된 타입의 인스턴스로 base 메서드를 호출하는 방법

말이 좀 어렵군요. 코드로 설명하는 것이 빠르겠지요? ^^ 다음과 같은 코드가 있을 때,

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication1
{
    class BaseClass
    {
        public virtual void Do()
        {
            Console.WriteLine("BaseClass.Do");
        }
    }

    class DerivedClass : BaseClass
    {
        public override void Do()
        {
            Console.WriteLine("DerivedClass.Do");

            base.Do();
        }
    }
    static class Program
    {
        static void Main(string[] args)
        {
            DerivedClass dc = new DerivedClass();
            dc.Do();
        }
    }
}

실행하면 화면에는 다음과 같은 출력 결과가 나옵니다.

DerivedClass.Do
BaseClass.Do

그런데, DerivedClass.Do 메서드의 작업을 base.Do만 실행하도록 하는 방법은 없을까요? 당연히 virtual/override 메서드이기 때문에 다음과 같이 호출하는 것은 의도한 대로 동작하지 않습니다.

BaseClass dc = new DerivedClass();
dc.Do();

또한, 리플렉션을 어설프게 이용하는 수준으로는 마찬가지 결과만 얻게 됩니다.

MethodInfo mi = typeof(BaseClass).GetMethod("Do");
mi.Invoke(dc, null);

혹시나 싶어 검색을 해보았는데, 이미 누군가가 C# 3.0의 확장 메서드로 친절하게 만들어 놓았습니다. ^^

Invoke base method using reflection
; http://www.simplygoodcode.com/2012/08/invoke-base-method-using-reflection.html

위의 글에 공개된 소스코드는 다음과 같습니다.


public static object InvokeNotOverride(this MethodInfo methodInfo,
        object targetObject, params object[] arguments)
{
    var parameters = methodInfo.GetParameters();

    if (parameters.Length == 0)
    {
        if (arguments != null && arguments.Length != 0)
            throw new Exception("Arguments cont doesn't match");
    }
    else
    {
        if (parameters.Length != arguments.Length)
            throw new Exception("Arguments cont doesn't match");
    }

    Type returnType = null;
    if (methodInfo.ReturnType != typeof(void))
    {
        returnType = methodInfo.ReturnType;
    }

    var type = targetObject.GetType();
    var dynamicMethod = new DynamicMethod("", returnType,
            new Type[] { type, typeof(Object) }, type);

    var iLGenerator = dynamicMethod.GetILGenerator();
    iLGenerator.Emit(OpCodes.Ldarg_0); // this

    for (var i = 0; i < parameters.Length; i++)
    {
        var parameter = parameters[i];

        iLGenerator.Emit(OpCodes.Ldarg_1); // load array argument

        // get element at index
        iLGenerator.Emit(OpCodes.Ldc_I4_S, i); // specify index
        iLGenerator.Emit(OpCodes.Ldelem_Ref); // get element

        var parameterType = parameter.ParameterType;
        if (parameterType.IsPrimitive)
        {
            iLGenerator.Emit(OpCodes.Unbox_Any, parameterType);
        }
        else if (parameterType == typeof(object))
        {
            // do nothing
        }
        else
        {
            iLGenerator.Emit(OpCodes.Castclass, parameterType);
        }
    }

    iLGenerator.Emit(OpCodes.Call, methodInfo);
    iLGenerator.Emit(OpCodes.Ret);

    return dynamicMethod.Invoke(null, new object[] { targetObject, arguments });
}

원리는 간단합니다. 런타임 시에 메서드를 바인딩하는 OpCodes.Callvirt 호출을 사용하지 않고 BaseClass.Do 메서드의 Handle 값을 그대로 OpCodes.Call 코드로 전달해 호출하는 동적 메서드를 만든 것입니다.

멋지군요. ^^ (첨부 파일은 위의 예제코드를 테스트 한 것입니다.)




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






[최초 등록일: ]
[최종 수정일: 6/9/2021]

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

비밀번호

댓글 작성자
 




... 46  47  48  49  50  51  52  53  54  55  [56]  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12537정성태2/11/202119357.NET Framework: 1022. UI 요소의 접근은 반드시 그 UI를 만든 스레드에서! - 두 번째 이야기 [2]
12536정성태2/9/202118201개발 환경 구성: 542. BDP(Bandwidth-delay product)와 TCP Receive Window
12535정성태2/9/202117282개발 환경 구성: 541. Wireshark로 확인하는 LSO(Large Send Offload), RSC(Receive Segment Coalescing) 옵션
12534정성태2/8/202117803개발 환경 구성: 540. Wireshark + C/C++로 확인하는 TCP 연결에서의 closesocket 동작 [1]파일 다운로드1
12533정성태2/8/202116801개발 환경 구성: 539. Wireshark + C/C++로 확인하는 TCP 연결에서의 shutdown 동작파일 다운로드1
12532정성태2/6/202118006개발 환경 구성: 538. Wireshark + C#으로 확인하는 ReceiveBufferSize(SO_RCVBUF), SendBufferSize(SO_SNDBUF) [3]
12531정성태2/5/202116783개발 환경 구성: 537. Wireshark + C#으로 확인하는 PSH flag와 Nagle 알고리듬파일 다운로드1
12530정성태2/4/202120597개발 환경 구성: 536. Wireshark + C#으로 확인하는 TCP 통신의 Receive Window
12529정성태2/4/202118478개발 환경 구성: 535. Wireshark + C#으로 확인하는 TCP 통신의 MIN RTO [1]
12528정성태2/1/202118073개발 환경 구성: 534. Wireshark + C#으로 확인하는 TCP 통신의 MSS(Maximum Segment Size) - 윈도우 환경
12527정성태2/1/202118157개발 환경 구성: 533. Wireshark + C#으로 확인하는 TCP 통신의 MSS(Maximum Segment Size) - 리눅스 환경파일 다운로드1
12526정성태2/1/202114976개발 환경 구성: 532. Azure Devops의 파이프라인 빌드 시 snk 파일 다루는 방법 - Secure file
12525정성태2/1/202113932개발 환경 구성: 531. Azure Devops - 파이프라인 실행 시 빌드 이벤트를 생략하는 방법
12524정성태1/31/202115140개발 환경 구성: 530. 기존 github 프로젝트를 Azure Devops의 빌드 Pipeline에 연결하는 방법 [1]
12523정성태1/31/202116064개발 환경 구성: 529. 기존 github 프로젝트를 Azure Devops의 Board에 연결하는 방법
12522정성태1/31/202118258개발 환경 구성: 528. 오라클 클라우드의 리눅스 VM - 9000 MTU Jumbo Frame 테스트
12521정성태1/31/202117339개발 환경 구성: 527. 이더넷(Ethernet) 환경의 TCP 통신에서 MSS(Maximum Segment Size) 확인 [1]
12520정성태1/30/202116096개발 환경 구성: 526. 오라클 클라우드의 VM에 ping ICMP 여는 방법
12519정성태1/30/202114822개발 환경 구성: 525. 오라클 클라우드의 VM을 외부에서 접근하기 위해 포트 여는 방법
12518정성태1/30/202132924Linux: 37. Ubuntu에 Wireshark 설치 [2]
12517정성태1/30/202120636Linux: 36. 윈도우 클라이언트에서 X2Go를 이용한 원격 리눅스의 GUI 접속 - 우분투 20.04
12516정성태1/29/202117103Windows: 188. Windows - TCP default template 설정 방법
12515정성태1/28/202118754웹: 41. Microsoft Edge - localhost에 대해 http 접근 시 무조건 https로 바뀌는 문제 [3]
12514정성태1/28/202118905.NET Framework: 1021. C# - 일렉트론 닷넷(Electron.NET) 소개 [1]파일 다운로드1
12513정성태1/28/202116062오류 유형: 698. electronize - User Profile 디렉터리에 공백 문자가 있는 경우 빌드가 실패하는 문제 [1]
12512정성태1/28/202116450오류 유형: 697. The program can't start because VCRUNTIME140.dll is missing from your computer. Try reinstalling the program to fix this problem.
... 46  47  48  49  50  51  52  53  54  55  [56]  57  58  59  60  ...