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

これは私には意味がありません。多くの人をループに陥れると思います。変数 vehicle を VehicleBase 型として宣言すると、次のようになります。

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

メソッド Accelerate は非仮想であるため、これは前のケースでも予想されることです。

前の出力では、(セダンとして型指定された変数 vehicle を使用して、VehicleBase.Accelerate の代わりに Sedan.Accelerate が呼び出されることを期待します。現在のところ、どこから呼び出すかによって異なります (クラス内から、または外部から)行動が変化しています。

再導入されたメソッドのオーバーロード解決規則が優先されているように思えますが、これが正しい/予期される動作であるとは信じがたいです。

4

2 に答える 2

4

これはすべて完全に理にかなっています。車両を として宣言するとSedan、 の 2 つの呼び出しAccelerateは異なる方法で解決されます。

  • メソッドからへの呼び出しAccelerateが行われると、 にメソッドDriveがあることを認識しないため、ベースの対応するメソッドを呼び出します。newSedan
  • メソッドからへの呼び出しAccelerateが行われるMainと、コンパイラは、変数newの正確な型が であることを認識しているため、メソッドを呼び出していることを認識します。vehicleSedan

一方、メソッドAccelerateから への呼び出しが行われているMainが、変数が であると宣言されているVehicleBase場合、コンパイラは型が であると想定できないため、再び基底クラスのメソッドに をSedan解決します。Accelerate

于 2012-12-15T20:14:57.943 に答える
1

非仮想メソッドはコンパイル時に式のタイプに応じて解決され、呼び出し先はコンパイル時に固定されます。コンパイラーは、呼び出しのソースコードコンテキストでコンパイル時に使用可能なシンボル情報でメソッド名を検索し、検出できた非仮想メソッドを呼び出すための呼び出し命令を発行します。インスタンスの実際の型が何であるかは関係ありません。呼び出しは常に、静的型情報からコンパイル時に計算された非仮想メソッドに送られます。

Accelerateこれが、への呼び出しSedan.AccelerateがMainのコンテキストで行われるのにVehicleBase.AccelerateVehicleBase.Drive:のコンテキストで行われる理由です。

関数の本体で、Main型の変数を宣言し、Sedanその変数を使用してメソッド呼び出しを行います。コンパイラーは、呼び出しに使用された変数のタイプで「Accelerate」という名前のメソッドを探し、Sedanと入力して、を見つけSedan.Accelerateます。

メソッド内では、「self」VehicleBase.Driveコンパイル時VehicleBaseの型はです。VehicleBase.Accelerateこれは、コンパイラがこのソースコードコンテキストで認識できる唯一のタイプであるため、ランタイムオブジェクトインスタンスのタイプが実際にである場合でも、「VehicleBase.Drive」でのAccelerateの呼び出しは常にになりますSedan

型で宣言されたメソッドの本体では、コンパイラは最初に型のメソッドを調べ、次に一致するものが見つからなかった場合は型Sedanを調べることによって、非仮想メソッド呼び出しを解決します。SedanVehicleBaseSedan

実際のオブジェクトインスタンスタイプに基づいて呼び出し先を変更する場合は、仮想メソッドを使用する必要があります。仮想メソッドを使用すると、実行時にオブジェクトインスタンスによって決定される最も具体的な実装に常に呼び出されるため、より一貫性のある実行結果が得られます。

非仮想メソッド呼び出しの宛先は、実行時を意識せずに、コンパイル時のタイプに従って選択されます。仮想メソッドの呼び出し先は、実行時にインスタンスタイプに応じて選択されます。

于 2012-12-17T18:28:07.917 に答える