2013-10-16 3 views
72

This question 제네릭 메서드의 구체적인 구현이 실제로 존재하게되는 것에 대해 궁금해했습니다. 나는 구글을 시도했지만 올바른 검색을하지 않을 것입니다.어떻게, 언제, 어디서 일반적인 방법을 구체적으로 만들었습니까?

우리는이 간단한 예제를 가지고가는 경우 :

class Program 
{ 
    public static T GetDefault<T>() 
    { 
     return default(T); 
    } 

    static void Main(string[] args) 
    { 
     int i = GetDefault<int>(); 
     double d = GetDefault<double>(); 
     string s = GetDefault<string>(); 
    } 
} 

내 머리에 난 항상 어떤 점에서 그것은 3 필요한 구체적인 구현과 구현을 초래한다고 가정 한

등, 그 순진한 의사 맹 글링에서, 우리 특정 유형
class Program 
{ 
    static void Main(string[] args) 
    { 
     int i = GetDefaultSystemInt32(); 
     double d = GetDefaultSystemFloat64(); 
     string s = GetDefaultSystemString(); 
    } 

    static int GetDefaultSystemInt32() 
    { 
     int i = 0; 
     return i; 
    } 
    static double GetDefaultSystemFloat64() 
    { 
     double d = 0.0; 
     return d; 
    } 
    static string GetDefaultSystemString() 
    { 
     string s = null; 
     return s; 
    } 
} 

은 여전히 ​​일반적인 유형의 용어로 표현되는 일반 프로그램의 IL 찾고 올바른 스택 할당 등의 결과를 사용하여이 논리 콘크리트 implementaiton을 가질 것이다 :

.method public hidebysig static !!T GetDefault<T>() cil managed 
{ 
    // Code size  15 (0xf) 
    .maxstack 1 
    .locals init ([0] !!T CS$1$0000, 
      [1] !!T CS$0$0001) 
    IL_0000: nop 
    IL_0001: ldloca.s CS$0$0001 
    IL_0003: initobj !!T 
    IL_0009: ldloc.1 
    IL_000a: stloc.0 
    IL_000b: br.s  IL_000d 
    IL_000d: ldloc.0 
    IL_000e: ret 
} // end of method Program::GetDefault 

그렇다면 int, double, 그리고 문자열을 스택에 할당하고 호출자에게 반환해야하는 시점과 시점은 어떻게 결정됩니까? 이것이 JIT 프로세스의 운영입니까? 나는 완전히 잘못된 빛에서 이것을보고 있는가?

+5

나처럼, 당신은 C++ 용어로 이것을 생각하는 것처럼 보입니다. 나는 답을 모른다. 그러나 C#의 제네릭에 관한 몇 가지 예기치 않은 사실을 상기해라. –

+4

IL이 제네릭을 지원합니다. 커다란 진전은 이미 컴파일 된 어셈블리의 클래스가 여전히 generics를 지원한다는 것입니다. _ (전체 .NET 프레임 워크와 유사) _ –

+0

@Jonathan Wood 전적으로 C++에서 메소드 이름을 변경하는 방법의 관점에서이를 살펴 봅니다! – dkackman

답변

77

C#에서는 제네릭 형식 및 메서드의 개념이 런타임 자체에서 지원됩니다. C# 컴파일러는 실제로 제네릭 메서드의 구체적인 버전을 만들 필요가 없습니다.

실제 "구체적인"일반 메소드는 런타임시 JIT에서 작성되며 IL에 존재하지 않습니다. 유형에 일반 메소드가 처음 사용될 때, JIT는 작성된 것인지를 확인하고 그렇지 않으면 해당 일반 유형에 대해 적절한 메소드를 구성합니다.

이것은 generics와 C++의 템플릿과 같은 기본적인 차이점 중 하나입니다. 또한 제네릭의 많은 제한 사항의 주된 이유이기도합니다. 컴파일러가 실제로 유형에 대한 런타임 구현을 작성하지 않았기 때문에 인터페이스 제한 사항은 컴파일 시간 제약 조건에 의해 처리되므로 제네릭은 C++의 템플릿보다 제 한적으로 제한됩니다 잠재적 유스 케이스의 그러나 런타임 자체에서 지원된다는 사실은 C++ 및 기타 컴파일 타임으로 생성 된 템플릿 구현에서 지원되지 않는 방식으로 라이브러리에서 제네릭 형식 및 사용을 생성 할 수있게합니다.

+1

사실, C++ 템플릿과 C# 제네릭 간의 질적 차이는 시간과 메모리 제약이 없다는 것입니다. 상대적으로 작은 실행 파일이 무제한으로 인식 할 수있는 유형의 인스턴스를 생성 할 수 있습니다 (예 : 프로그램이 임의의 길이의 입력 문자열 (예 : "FRED")을 가져 와서'F >>>'의 인스턴스를 생성하십시오. – supercat

+2

좋은 답변 리드를 알고 계시지 만 알고 계신 참고 자료가 있습니까? – dkackman

+7

@dkackman 나는 http://www.artima.com/intv/generics.html을 통해 읽었을 것이다. 거기서 논의 되었어. –

45

일반 메소드의 실제 기계 코드는 메소드를 사용할 때 언제나처럼 작성됩니다. 이 시점에서 지터는 적합한 후보가 전에 jitted되었는지를 먼저 확인합니다. 매우 일반적인 경우, 구체적인 런타임 유형 T가 참조 유형 인 메소드의 코드는 한 번만 생성되어야하며 가능한 모든 참조 유형 T에 적합해야합니다. T의 제약 조건은이 기계 코드가 항상 유효하며, 이전에 C# 컴파일러에서 확인했습니다.

값 형식 인 T에 대해 추가 복사본이 생성 될 수 있습니다. T 값이 더 이상 간단한 포인터가 아니기 때문에 컴퓨터 코드가 다릅니다.

네, 그렇다면 세 가지 방법으로 끝나게 될 것입니다. <string> 버전은 모든 참조 유형에 사용할 수 있지만 다른 유형은 사용할 수 없습니다. 그리고 <int><double> 버전은 "T의 값 유형"범주에 해당합니다.

다른 좋은 예는이 메소드의 반환 값이 호출자에게 다르게 전달됩니다. x64 지터에서 문자열 버전은 반환 된 포인터 값과 마찬가지로 RAX 레지스터 값을 반환하며 int 버전은 EAX 레지스터로 반환하고 double 버전은 XMM0 레지스터를 반환합니다.

관련 문제