override 메서드가 정의된 타입의 인스턴스로 base 메서드를 호출하는 방법 - 두 번째 이야기
이번 글은 다음의 글에 대한 보강입니다. ^^
override 메서드가 정의된 타입의 인스턴스로 base 메서드를 호출하는 방법
; https://www.sysnet.pe.kr/2/0/1564
위의 글에서는 DynamicMethod를 이용해 callvirt를 call로 강제 지정하는 방법을 사용하고 있는데요. 이 기법을 .NET 1.1에서 사용하려면 어떻게 해야 할까요? ^^
아쉽게도 DynamicMethod는 .NET 2.0부터 지원되기 때문에 1.1 이하에서는 사용할 수 없습니다. (물론, 1.1 용으로 아직도 응용 프로그램을 만드는 경우는 거의 없겠지만.)
이에 대해 혹시나 또 누군가 친절하게 방법을 만들어 두지 않았을까 검색해 보았는데... 아쉽게도 이번에는 꽝이군요. ^^
C# .NET 1.1 - MethodBase.Invoke on virtual methods (dynamic method lookup)
; http://www.codeproject.com/Messages/1618442/Csharp-NET-1-1-MethodBase-Invoke-on-virtual-method.aspx
방법이 없을까...? 생각하다가 한 가지 아이디어가 떠 올랐습니다. 혹시 제가 무슨 생각을 한 것인지 상상이 가시나요? ^^
힌트를 하나 드리자면, 다음과 같이 테스트 코드를 만드는 것으로 시작할 수 있습니다.
using System;
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();
DirectInvoke(dc);
}
private static void DirectInvoke(DerivedClass dc)
{
dc.Do();
}
}
}
그러니까, 요점은 callvirt가 아닌 call을 해주면 되기 때문에 이를 IL 어셈블리를 직접 변경해서 구현할 수 있습니다. 따라서 산출된 DLL/EXE 파일에 대해 다음과 같이 IL 코드로 역어셈블을 하면,
C:\Users\bin\Debug>ildasm ConsoleApplication1.exe /OUTPUT=test.il
다음과 같은 결과물을 얻을 수 있습니다.
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
// ... 생략 ...
.class private auto ansi beforefieldinit ConsoleApplication1.DerivedClass
extends ConsoleApplication1.BaseClass
{
// ... 생략 ...
.method private hidebysig static void DirectInvoke(class ConsoleApplication1.DerivedClass dc) cil managed
{
// Code size 9 (0x9)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: callvirt instance void ConsoleApplication1.BaseClass::Do()
IL_0007: nop
IL_0008: ret
} // end of method Program::DirectInvoke
} // end of class ConsoleApplication1.Program
그럼, 위의 callvirt를 call로 간단하게 변경해 주고,
.method private hidebysig static void DirectInvoke(class ConsoleApplication1.DerivedClass dc) cil managed
{
// Code size 9 (0x9)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance void ConsoleApplication1.BaseClass::Do()
IL_0007: nop
IL_0008: ret
} // end of method Program::DirectInvoke
다시 IL 컴파일을 해주면,
C:\Users\bin\Debug>ilasm test.il
Microsoft (R) .NET Framework IL Assembler. Version 4.0.30319.33440
Copyright (c) Microsoft Corporation. All rights reserved.
Assembling 'test.il' to EXE --> 'test.exe'
Source file is ANSI
Assembled method ConsoleApplication1.BaseClass::Do
Assembled method ConsoleApplication1.BaseClass::.ctor
Assembled method ConsoleApplication1.DerivedClass::Do
Assembled method ConsoleApplication1.DerivedClass::.ctor
Assembled method ConsoleApplication1.Program::Main
Assembled method ConsoleApplication1.Program::DirectInvoke
Creating PE file
Emitting classes:
Class 1: ConsoleApplication1.BaseClass
Class 2: ConsoleApplication1.DerivedClass
Class 3: ConsoleApplication1.Program
Emitting fields and methods:
Global
Class 1 Methods: 2;
Class 2 Methods: 2;
Class 3 Methods: 2;
Resolving local member refs: 5 -> 5 defs, 0 refs, 0 unresolved
Emitting events and properties:
Global
Class 1
Class 2
Class 3
Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
Writing PE file
Operation completed successfully
이제 원하는대로 다형성이 무시된 base 메서드만을 호출하게 동작합니다. ^^
C:\Users\bin\Debug>test
BaseClass.Do
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]