4

したがって、基本的に、呼び出されているメソッドが非仮想インスタンス メソッドか仮想メソッドかを判断するために C# コンパイラが実行する一般的な手順を理解したいと思います。

混乱は、これら 2 つの説明 (C# による CLR 第 3 版、Jeffrey Richter、Chapter 4 Type Fundamentals) から生じます。

非仮想インスタンス メソッドを呼び出す場合、JIT コンパイラは、呼び出しに使用されている変数の型に対応する型オブジェクトを見つけます。

仮想メソッド呼び出しの場合

仮想インスタンス メソッドを呼び出すと、JIT コンパイラはメソッド内に追加のコードを生成します。このコードは、メソッドが呼び出されるたびに実行されます。このコードは、最初に呼び出しを行うために使用されている変数を調べ、次に呼び出し元オブジェクトへのアドレスに従います。

小さなテストプロジェクトを作成しました

class Program
    {
        static void Main(string[] args)
        {
            Parent p = new Derived();
            p.Foo(10); // Outputs Derived.Foo(int x)

            Derived d = new Derived();
            d.Foo(10); // Outputs Derived.Foo(double y)
        }
    }

    internal class Parent
    {
        public virtual void Foo(int x)
        {
            Console.WriteLine("Parent.Foo(int x");
        }
    }

    internal class Derived: Parent
    {
        public override void Foo(int x)
        {
            Console.WriteLine("Derived.Foo(int x)");
        }

        public void Foo(double y)
        {
            Console.WriteLine("Derived.Foo(double y)");
        }
    }

Jon Skeet は、なぜプログラムがこれらの出力を生成するのかを説明するブログ投稿を行っており、Eric Lippert は彼のブログ投稿(コメント セクションを確認してください) で、呼び出されているメソッドが非仮想であるかどうかをコンパイラがどのように判断するかをまだ理解できません。インスタンス メソッドまたは仮想メソッド。

非仮想インスタンスメソッド呼び出しの場合、コンパイラはメソッドの呼び出しに使用されている変数の型をチェックし、仮想メソッドの場合は、メソッドの呼び出しに使用されている変数によって参照されるオブジェクトの型をチェックしているようです。 、メソッドの実行方法を決定する前に、メソッドが非仮想か仮想かを判断する何らかの方法が必要です。

4

1 に答える 1

10

メソッドの実行方法を決定する前に、メソッドが非仮想か仮想かを判断する方法が必要です。

JIT コンパイラは C# コンパイラと同じではありません。実行が JIT コンパイラに到達するまでに、C# コンパイラは、仮想メソッド呼び出しを発行するか、非仮想メソッド呼び出しを発行するかを既に決定しています。Richter の本が伝えている唯一のことは、仮想メソッドと非仮想メソッドを呼び出すために JIT コンパイラが行うさまざまなことです。C# コンパイラが仮想メソッド呼び出しまたは非仮想メソッド呼び出しを発行するかどうかを決定する方法を示しているわけではありません。

一般に、そのためには、言語仕様を確認する必要があります。コンパイラがプログラム コードを呼び出し元として解釈するメソッドが仮想である場合、仮想呼び出しが発行されます。そうでなければ、そうはなりません。この特定のケースでの言語のルールは、それが Jon の投稿の要点であり、コンパイラがDerived.Foo(double)2 回目の呼び出しで非仮想メソッド呼び出しを発行することを指示します。これは、この場合、コンパイラがプログラム コードを (言語仕様に基づいて) 呼び出していると解釈するためDerived.Foo(double)です。JIT はそれについて何も考えていません。IL が非仮想呼び出しを実行Derived.Foo(double)し、参照dを暗黙的thisパラメーターおよび10最初の明示的パラメーターとして使用しているだけです。

于 2013-05-31T21:06:05.580 に答える