2012-12-15 2 views
2

비가 상 메서드를 해결하는 방법을 이해하려면 (C#) 변수의 유형 (인스턴스 유형 아님)에 따라 달라진다는 사실을 이해해야합니다.비 가상 메서드 확인 -이 문제가 발생하는 이유

아래 코드를 살펴보십시오.

class Program 
{ 
    static void Main(string[] args) 
    { 
     Sedan vehicle = new Sedan(); 
     vehicle.Drive(); 
     vehicle.Accelerate(); 
    } 
} 

abstract class VehicleBase 
{ 
    public void Drive() 
    { 
     ShiftIntoGear(); 
     Accelerate(); 
     Steer(); 
    } 

    protected abstract void ShiftIntoGear(); 
    protected abstract void Steer(); 

    public void Accelerate() 
    { 
     Console.WriteLine("VehicleBase.Accelerate"); 
    } 
} 

class Sedan : VehicleBase 
{ 
    protected override void ShiftIntoGear() 
    { 
     Console.WriteLine("Sedan.ShiftIntoGear"); 
    } 

    protected override void Steer() 
    { 
     Console.WriteLine("Sedan.Steer"); 
    } 

    public new void Accelerate() 
    { 
     Console.WriteLine("Sedan.Accelerate"); 
    } 
} 

콘솔 윈도우는 다음을 보여줍니다

Sedan.ShiftIntoGear 
VehicleBase.Accelerate 
Sedan.Steer 
Sedan.Accelerate 

이 나에게 이해가되지 않습니다 내가 루프 많은 사람들을 던질 것이라고 생각합니다. 당신이 방법 가속이 아닌 가상이기 때문에 나뿐만 아니라 이전의 경우에 기대할 수있는 것입니다

Sedan.ShiftIntoGear 
VehicleBase.Accelerate 
Sedan.Steer 
VehicleBase.Accelerate 

를 얻을 VehicleBase 당신은 지금 형으로 변수 차량을 선언합니다.

이전 출력에서 ​​(세단 형 자동차로 입력 된 변수 차량에서는 VehicleBase.Accelerate 대신 Sedan.Accelerate가 호출 될 것으로 예상됩니다. 클래스 또는 외부에서) 행동이 바뀌고 있습니다.

다시 소개 된 메서드에 대한 과부하 해결 규칙이 우선시되는 것으로 보이지만 이것이 올바른/예상 된 동작이라고 믿는 데 어려움이 있습니다.

+0

여기에 당황한 것을 잘 모릅니다. 문제가되는 것을 설명해 주시겠습니까? – Oded

+0

나는 내가 그랬다고 생각했다. 두 가지 결과물이 제공되었고 나의 기대도있었습니다. 내 기대를 더 명확하게 설명하기 위해 내 게시물을 편집합니다. –

+0

'VehicleBase'에서'Accelerate' 가상을 만들고'Sedan'에서 그것을 오버라이드하면 기대했던 동작을 얻을 수 있습니다. – juharr

답변

1

비 가상 메서드는 컴파일 타임에 표현식의 유형에 따라 결정되고 호출 대상은 컴파일 타임에 고정됩니다. 컴파일러는 컴파일 타임에 호출의 소스 코드 컨텍스트에서 사용할 수있는 심볼 정보에서 메소드 이름을 조회하고 찾을 수있는 비 가상 메소드를 호출하는 호출 명령어를 내 보냅니다. 인스턴스의 실제 유형이 무엇인지는 중요하지 않으며 호출은 항상 정적 유형 정보에서 컴파일 타임에 계산 된 비 가상 메소드로 이동합니다. 여기

Accelerate에 대한 호출이 홈페이지의 맥락에서 Sedan.Accelerate에가는 이유이지만, VehicleBase.Drive의 맥락에서 VehicleBase.Accelerate에 간다 : 당신의 Main 함수의 본문에서

, 당신은 유형의 변수를 선언 한 Sedan, 그리고 당신은 그 변수를 사용하여 메소드를 호출합니다.컴파일러는 호출에 사용 된 변수 유형에서 "Accelerate"라는 메서드를 찾고 Sedan을 입력하고 Sedan.Accelerate을 찾습니다.

VehicleBase.Drive 내부에 "self"의 컴파일 시간은 VehicleBase입니다. 컴파일러가이 소스 코드 컨텍스트에서 볼 수있는 유일한 유형이므로 런타임 객체 인스턴스의 유형이 실제로 Sedan 인 경우에도 'VehicleBase.Drive'에서 Accelerate를 호출하면 항상 VehicleBase.Accelerate으로 이동합니다. Sedan 유형에 선언하는 방법의 몸에서

, 컴파일러는 일치하는 Sedan에서 찾을 수 없습니다 경우 VehicleBase 유형을보고 한 후, 먼저 Sedan 유형의 방법에서 보면 비 가상 메서드 호출을 해결하는 것 .

실제 대상 인스턴스 유형에 따라 통화 대상을 변경하려면 가상 방법을 사용해야합니다. 가상 메서드를 사용하면 호출이 항상 런타임에 객체 인스턴스에 의해 결정되는 가장 구체적인 구현으로 이동하므로 훨씬 더 일관된 실행 결과를 얻을 수 있습니다.

비 가상 메서드 호출 대상은 런타임을 인식하지 않고 컴파일 타임 형식에 따라 선택됩니다. 가상 메서드 호출 대상은 런타임에 인스턴스 유형에 따라 선택됩니다.

+0

설명에 감사 드리며 Drive 메소드에서 VehicleBase.Accelerate가 호출 된 이유를 설명해 주셔서 감사합니다. 이것을 얻는 열쇠는 compile-time * 타입의 self/this가 VehicleBase이고 변수의 타입이 아니라는 것입니다. 그래서 지금은 설명을 이해하지만, 나는 컴파일러가 왜 (드라이브 메소드 내부에서) Sedan이 될지 알지 못한다. :). –

+0

델파이도 비슷하게 동작합니까? 필자는 자주 정적 메서드 디스 패칭을 사용하여이 기능을 사용하지 않았으며 기본 클래스에서 호출하는 동안 하위 클래스의 메서드를 다시는 도입하지 않았습니다. Java는 무엇입니까? (당신은 알고 있습니까?) –

+0

C#과 Delphi의 동작의 유일한 차이점은 Delphi가 C#보다 6 년 앞서 그것을 수행했다는 것입니다. ;> – dthorpe

4

차량을 Sedan으로 선언하면 Accelerate에 대한 두 번의 호출이 다르게 처리됩니다.

, Accelerate 호출이 Drive 방법 만들어진다
  • , 상기 Sedannew 방법이 있음을 모르고있다, 그래서 상기베이스
  • 의 대응 방식에 대한 호출을하는 경우 호출 AccelerateMain 메서드에서 만들어 지므로 vehicle 변수의 정확한 형식이 Sedan이라는 것을 알고 있기 때문에 컴파일러는 new 메서드를 호출하고 있음을 알고 있습니다. 한편

, Accelerate에 대한 호출이 Main 방법을 사용하고 있지만, 변수가 VehicleBase로 선언, 컴파일러는 타입이 Sedan라고 가정 할 수 없기 때문에이 방법에 Accelerate 해결 다시 기본 클래스의.

+0

Accelerate가 가상 메서드가 아니고 변수가 Sedan으로 입력 되었기 때문에 컴파일러는 메서드가 다시 도입 (new)된다는 것을 알고 두 곳에서 다시 도입 된 Accelerate 메서드를 호출해야합니다. –

+0

@ ShivKumar 컴파일러는 변수를 '세단'으로 선언 할 때만, 즉 코드가 게시물에 쓰여지는 방식으로 만 알 수 있습니다. 그러나 변수를 'VehicleBase vehicle = new Sedan()'으로 선언하면 컴파일러는 'vehicle'유형이 'VehicleBase'임을 알고 있기 때문에 새로 추가 된 메소드를 호출 할 수 없습니다. – dasblinkenlight

+0

변수가 세단 형으로 입력 된 경우는 (제 생각에는) 예상치 못한 행동입니다. 그래서 컴파일 타임에 세단 형을 알았고 문제의 방법이 가상이 아닌 경우 어떻게 동작합니까? 어쩌면 당신이 내 게시물을 다시 읽으면 도움이 될 것입니다. :) –