2013-02-28 1 views
1

, 난 치형 (int)의 가상 메소드 호출값 유형에 대한 가상 호출이 값 및 박스 값에 대한 포인터와 함께 작동하는 이유는 무엇입니까? 아래 예에서

namespace ShortTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 42; 
      i.ToString(); 
      ((object)i).ToString(); 
     } 
    } 
} 

리플렉터 생성 CIL 찾고, I는 두 전화에 다음 코드를 볼 수

.locals init ([0] int32 i, ...) 
... 
L_001a: ldloca.s i 
L_001c: call instance string [mscorlib]System.Int32::ToString() 
L_0021: pop 
L_0022: ldloc.0 
L_0023: box int32 
L_0028: callvirt instance string [mscorlib]System.Object::ToString() 
L_002d: pop 
... 

this 매개 변수는 첫 번째 경우에는 int에 대한 관리되는 포인터이고 두 번째 경우에는 박스형 int (즉, 헤더 필드와 int 필드가있는 개체에 대한 포인터)에 대한 참조입니다.

동일한 방법이 두 호출 (intToString()을 구현합니다)에 사용되었으므로 어떻게 작동합니까? System.Int32::ToString() 메서드는 this 포인터에서 ldind.i4을 사용하여 int의 값을 검색하므로 첫 번째 경우에는 int의 값을 가져야하지만 상자에있는 int의 첫 번째 필드 (머리글)의 값을 가져와야합니다. 두 번째 경우는 :

L_0000: ldarg.0 
L_0001: ldind.i4 
... 

답변

3

하지만 두 번째 경우

당신은 ToString 방법을 가정하고을의 박스 INT의 첫 번째 필드 (헤더)의 값을 얻어야한다는 점을 전달 어, 그리고 어떤 바이트가 가리키게 되든 읽혀지는 것이 있습니다. 그 가정은 잘못되었습니다.

값 유형의 인스턴스 메소드에는 ref T "this"매개 변수가있는 것처럼 작동합니다. .NET 런타임은 값 유형이 박스인지 여부에 관계없이 이것이 올바르게 전달되는지 확인하므로 메소드가 박스 처리 된 값에 특별한 처리를 할 필요가 없습니다.

귀하의 출발점은 올바른 :

I.8.9.7 값이 아닌 정적 메소드 (즉, 인스턴스 또는 가상 메소드가) 값을 호출 유형 정의

이 포인터는 인스턴스에 대한 관리되는 참조이며, 연관된 박스형에 대해 메서드가 호출 될 때 this 포인터는 개체 참조입니다.

그러나 이것은 같은 ldind.i4 명령이 관계없이 작동하는 방식으로 발생합니다. 관리되지 않는 포인터를 참조하는 경우에만 바이트를 읽습니다.

당신은 인스턴스와 클래스의 가상 메소드가이 포인터와 클래스의 인스턴스에 대한 참조를 기대하는 코딩해야한다

값 형식의

II.13.3 방법을 발견했다. 대조적으로, 값 유형의 인스턴스 및 가상 메소드 은 값 유형의 박스 화되지 않은 인스턴스 인 으로 관리 된 포인터 (파티션 I 참조)를 예상하도록 코딩됩니다.CLI는 boxed 값 유형이 값 유형에 의해 구현 된 가상 메소드에이 포인터로 으로 전달 될 때 박스 화 된 값 유형을 관리되지 않은 값 유형에 대한 관리 된 포인터로 변환해야합니다.

언뜻보기에는 위와 모순되지만 실제로 구현하려면 작동시키기 위해 필요한 모든 마술을 사용해야한다고 말합니다.

+0

사양 (ECMA335)에서 설명 된 동작이 어디인지 말해 줄 수 있습니까? 감사 ! –

+0

참조 (ECMA 335, 파티션 II, 섹션 13.3 값 유형의 메소드)를 찾았으므로이를 대답으로 편집했습니다. –

+0

@ GeorgesDupéron 그것보다 조금 복잡하다는 것이 드러났습니다. 편집하고있었습니다. – hvd

관련 문제