2011-03-15 3 views
3

가상 함수가 무엇인지 이해합니다. 그러나 내가 얻지 못하는 것은 그들이 내부적으로 어떻게 일하는가입니다. 상기 용가상 함수 C#

class Animal 
{ 
    virtual string Eat() 
    { 
     return @"Eat undefined"; 
    } 
} 

class Human : Animal 
{ 
    override string Eat() 
    { 
     return @"Eat like a Human"; 
    } 
} 


class Dog : Animal 
{ 
    new string Eat() 
    { 
     return @"Eat like a Dog"; 
    } 
} 

static void Main() 
{ 
    Animal _animal = new Human(); 
    Console.WriteLine(_animal.Eat()); 
    _animal = new Dog(); 
    Console.WriteLine(_animal.Eat()); 
} 

출력 준다 : 위의 코드에서 _animal

Eat like a Human 
Eat undefined 

인간 개체 또는 개 객체를 참조 형 동물이다. 이것은 무엇을 의미합니까? _animal에는 Human 또는 Dog 객체를 가리키는 주소가 들어 있습니다. 호출 할 함수를 어떻게 결정합니까? 첫 번째 경우에서 재정의하고 따라서 자식의 구현이 호출되지만 두 번째 경우에는 new를 사용하므로 부모의 구현이 호출됩니다. 후드에서 일어나는 일을 설명해 주시겠습니까? 사전 닉

I 메모리 _animal에 이해
+1

에릭 리 퍼트 (Eric Lippert)가이 주제에 대한 블로그 시리즈를 쓰고 있음을 알고 있습니까? http://blogs.msdn.com/b/ericlippert/archive/2011/03/17/implementing-the-virtual-method-pattern-in-c-part-one.aspx – Learner

+0

Learner에게 감사드립니다. 나는 그것을 따라 간다. – Nishant

답변

17

다음과 같이 작동합니다. 다음과 같이

Animal animal; 
Dog dog; 
animal = new Human(); 
animal.Eat(); 
animal = new Animal(); 
animal.Eat(); 
dog = new Dog(); 
dog.Eat(); 
animal = dog; 
animal.Eat(); 

컴파일러 이유 :

class VTable 
{ 
    public VTable(Func<Animal, string> eat) 
    { 
     this.AnimalEat = eat; 
    } 
    public readonly Func<Animal, string> AnimalEat; 
} 

class Animal 
{ 
    private static AnimalVTable = new VTable(Animal.AnimalEat); 
    private static string AnimalEat(Animal _this) 
    { 
     return "undefined"; 
    } 
    public VTable VTable; 
    public static Animal CreateAnimal() 
    { 
     return new Animal() 
      { VTable = AnimalVTable }; 
    } 
} 

class Human : Animal 
{ 
    private static HumanVTable = new VTable(Human.HumanEat); 
    private static string HumanEat(Animal _this) 
    { 
     return "human"; 
    } 
    public static Human CreateHuman() 
    { 
     return new Human() 
      { VTable = HumanVTable }; 
    } 
} 

class Dog : Animal 
{ 
    public static string DogEat(Dog _this) { return "dog"; } 
    public static Dog CreateDog() 
    { 
     return new Dog() 
      { VTable = AnimalVTable } ; 
    } 
} 

지금 이러한 호출을 고려 수신기의 종류가해야 먹을 호출이 다음 동물 인 경우 상상 컴파일러는 이것으로 클래스를 다시 썼다 동물. 동물. 동물. 수신자의 유형이 Dog이면 호출은 DogEat이어야합니다. 그래서 컴파일러가 기록이 같은 : 어떻게 작동하는지 정확히입니다

Animal animal; 
Dog dog; 
animal = Human.CreateHuman(); // sets the VTable field to HumanVTable 
animal.VTable.AnimalEat(animal); // calls HumanVTable.AnimalEat 
animal = Animal.CreateAnimal(); // sets the VTable field to AnimalVTable 
animal.VTable.AnimalEat(animal); // calls AnimalVTable.AnimalEat 
dog = Dog.CreateDog(); // sets the VTable field to AnimalVTable 
Dog.DogEat(dog); // calls DogEat, obviously 
animal = dog; 
animal.VTable.AnimalEat(animal); // calls AnimalVTable.AnimalEat 

. 컴파일러는 백그라운드에서 vtable을 생성하고 은 오버로드 해결 규칙의 규칙에 따라 vtable을 통해 호출할지 여부를 컴파일 타임에 결정합니다.

vtable은 객체가 생성 될 때 메모리 할당자가 설정합니다. 합니다 (VTABLE이되지 후, 전에의 ctor를 호출 설정되어 있기 때문에 내 스케치는이 점에서 거짓말이다.)

은 "이"가상의 방법은 실제로 비밀리에 보이지 않는 형식 매개 변수로 전달의 방법.

의미가 있습니까?

+7

@ Eric : 안녕하세요, Eric, 수석 개발자가 아닌 주요 개발자의 정보를 업데이트 한 것으로 나타났습니다. 나는 3 일 동안 그렇게 아니 었으므로 주말에 업데이트했다고 가정합니다. O 새로운 위치/승진에 축하 인사를 전하고 싶습니다. 그렇게 말할 수 있다면 자격이 있습니다. 또한 이것이 C#의 모든 것이 당신을 통과 할 것이라는 것을 의미합니까? 나는 당신이저기서 최고의 개발자 중 한 명이기 때문에 그렇게되기를 바랍니다. –

+0

@ 조안 : 친절한 말에 감사드립니다. 물론 나는 C#의 최종 단어에서 멀리 떨어져있다. 나는이 팀의 후배 중 한 명이다. 프로그래밍 언어를 설계하고 구현하는 데 15 년 밖에 걸리지 않습니다. 저는 Anders Hejlsberg, Neal Gafter, Peter Golde와 함께 몇 가지 이름을 짓습니다. 그 녀석들은 나보다 훨씬 더 수석이야. –

+3

@ Eric : 여전히 자신이 주니어라고 생각하면 재미 있습니다. 나는 당신이 아래에있는 사람을 알지 못합니다 : ○ 내가 일하는 곳에서 소프트웨어 회사는 아니지만 교장이 매우 높습니다. 그러나 마이크로 소프트와 같은 소프트웨어 거물에서는 훨씬 더 많은 성적이 있다고 생각합니다. 어쨌든 너희들과 대화 할 수있는 것은 매우 귀중한 경험 일 것이다. 당신이 계급과 행복에서 위로 움직일 것을 고대합니다. –

0
에서

덕분에 인간 또는 개 개체를 가리 킵니다 주소가 포함되어 있습니다. 호출 할 함수를 어떻게 결정합니까?

데이터와 마찬가지로 코드에도 주소가 있습니다.

따라서이 문제에 대한 일반적인 접근 방법은 Human 또는 Dog 개체의 메서드 코드 주소를 포함하는 것입니다. 때때로 vtable을 사용하여 호출됩니다. C 또는 C++와 같은 언어에서이 개념은 function pointer으로 직접 노출됩니다.

이제 꽤 높은 수준의 유형 시스템이있는 C#에 대해 언급했습니다.이 시스템에서는 런타임에 개체 유형도 식별 할 수 있습니다. 따라서 구현 방법은 기존 방법과 다소간 다를 수 있습니다. 그러나, 귀하의 질문에 함수 포인터/v 테이블 개념을 그것을 할 수있는 한 방법이며. NET에서 너무 많이 벗어났다면 그것은 놀랄 것입니다.

+0

고마워. 메모리 할당을 도와주세요. 코드에 주소가 있다고 했잖아요. 이 경우 메모리가 어떻게 할당됩니까? Dog 클래스에서는 기본 클래스 메서드 만 숨기고 Human 클래스에서는 재정의합니다. – Nishant

+0

@nick - EXE 또는 DLL이로드되면 OS는 해당 코드의 메모리를 관리합니다. (C#의 경우에는 JIT가 포함되어 있으므로 .NET 런타임도 관련 메모리를 관리합니다.) – asveikau

+0

마지막 문장의 요점을 설명하려면 인터페이스 작동 방식에 약간 놀랄 수 있습니다. 지터는 일반적으로 클래스 계층 구조에서 가상 메소드 호출을 위해 "클래식"vtable을 생성합니다. 그러나 인터페이스 메소드에서 가상 호출을 위해 생성 된 코드는 좀 더 복잡합니다. C++ 컴파일러가 생성하는 전형적인 간접 vtable과 다릅니다. –

0

C#에서 파생 클래스는 기본 클래스에서 상속 된 오버라이드 된 메서드에 한정자를 수정 자로 제공해야합니다.

Animal _animal = new Human(); 

는 그것은 단지 Human 개체가 구축되었다 아니에요. 그것들은 두 개의 하위 오브젝트입니다. 하나는 Animal 하위 오브젝트이고 다른 하나는 Human 하위 오브젝트입니다. _animal.Eat(); 호출 기본 클래스 방법 (즉, Eat())가 파생 클래스에서 재정의 여부 런타임 체크했다

Console.WriteLine(_animal.Eat()); 

. 재정의되었으므로 해당 파생 클래스 메서드가 호출됩니다. 경우

Eat like a Human 

하지만 - - 따라서 출력 Dog에서

_animal = new Dog(); 
Console.WriteLine(_animal.Eat()); 

는 파생 된 클래스 Dog에는 Eat() 재정의 방법이 없다. 그래서, 기본 클래스 메소드 자체가 호출됩니다. 또한이 검사 방법은 기본 클래스에서 Eat()이 가상으로 언급되고 호출 메커니즘이 런타임 인 ​​에 있기 때문에 수행됩니다. 요약하면 가상 호출 메커니즘은 런타임 메커니즘입니다.