7

この記事 とその記事を読んだ後、 私は混乱しました。

それは言う:

階層の異なるレベルに2つのメソッドがある場合、呼び出しの「より優れた関数メンバー」でなくても、「より深い」メソッドが最初に選択されます。

また -

子クラスの基本クラスメソッドをオーバーライドした場合、それを宣言したとは見なされないことがわかりました。

さて、私の質問に戻りましょう:

ケース1

    public class Base
     {
           public virtual void  Foo(int x)  { "1".Dump();}
     }

    public class Child : Base
     {
          public void Foo(object x) { "3".Dump();}  
          public override void  Foo(int x)  { "2".Dump();}
     }


void Main()
{
    Child c = new Child();
    c.Foo(10); //emits 3
}

OK。記事によると

「より良い関数」でなくても、「より深い」ものが最初に選択されます。オーバーライドはカウントされません。

したがって、それは正しく、プログラムは「3」を出力します。(Foo(object x)実行)

1行の行順を変えてみましょう:

ケース2

          public class Base
         {
                 public virtual void  Foo(int x)  { "1".Dump();}
                 public void Foo(object x) { "3".Dump();} //<line being moved here
         }

        public class Child : Base
         {
              public override void  Foo(int x)  { "2".Dump();}
         }


    void Main()
    {
        Child c = new Child();
        c.Foo(10); //emits 2 !!!!
    }

今では「2」を放出します。

次に、すべてのintをオブジェクトに変更し、すべてのオブジェクトをintに変更します。

ケース3

      public class Base
    {
      public virtual void  Foo(object x)  { "1".Dump();}
      public void Foo(int x) { "3".Dump();} 
    }

    public class Child : Base
    {
         public override void  Foo(object x)  { "2".Dump();}
    }


void Main()
{
    Child c = new Child();
    c.Foo(1); //emits "3"
}

質問


質問#1:ケース2の場合、父親からChild 継承し Foo(object x)、メソッドもオーバーライドします。

しかし、私たちはそれを言っていませんでした:

子クラスの基本クラスメソッドをオーバーライドした場合、それを宣言したとは見なされないことがわかりました。

???

実際、継承された関数も宣言していません...では、この状況でのルールは何ですか?


質問#2:ケース3の場合、父親からChild 継承し Foo(int x)、メソッドもオーバーライドします。

しかし今、彼はその父親の機能を選択しています...。

override完全に一致する場合にのみ勝っているようです。

繰り返しますが、この状況でのルールは何ですか?


4

3 に答える 3

3

タイプT(この場合はタイプのメンバー)の名前Nのメンバールックアッププロセスを参照してください。FooChild

最初に、Tで宣言されたNという名前のすべてのアクセス可能な(セクション3.5)メンバーとTの基本タイプ(セクション7.3.1)のセットが構築されます。

virtual void Foo(int x) // Base
void Foo(object x) // Base
override void Foo(int x) // Child

オーバーライド修飾子を含む宣言は、セットから除外されます。

virtual void Foo(int x) // Base
void Foo(object x) // Base

引数は整数型です。したがって、ここでの最良の選択は(引数タイプがパラメータータイプと一致する)です。

virtual void Foo(int x) // Base

そして、このメソッドが呼び出されました。しかし、それは仮想メソッドです。そして、仮想メソッド呼び出しメカニズムのために呼び出されます。

クラスで宣言された、またはクラスによって継承されたすべての仮想メソッドには、そのクラスに関してメソッドの最も派生した実装が存在します。クラスRに関する仮想メソッドMの最も派生した実装は、次のように決定されます。

  • RにMの導入仮想宣言が含まれている場合、これはMの最も派生した実装です。
  • それ以外の場合、RにMのオーバーライドが含まれている場合、これはMの最も派生した実装です。
  • それ以外の場合、Rに関するMの最も派生した実装は、Rの直接基本クラスに関するMの最も派生した実装と同じです。

virtual void Foo(int x)そして、クラスに関してメソッドの最も派生した実装は何Childですか?はい、そうです

override void Foo(int x) // Child

呼び出されます。3番目のサンプルにも同じルールが適用されます。ただし、オーバーライドされたメソッドの削除後に2つのオプションが残っている場合、(引数のタイプのために)最良の選択は非仮想メソッドです。

于 2012-05-28T14:58:34.603 に答える
3

起こっていることが2つあります:

  1. 呼び出すメソッドを選択するとき、コンパイラーはリンクされた記事に従ってルールに従います。浅い方法がより具体的な一致であっても、浅い方法の前に深い方法が選択されます
  2. オーバーライドされたメソッドを呼び出すと、常にオーバーライドされたメソッドが呼び出されます(これは、呼び出すメソッドの選択とは別です)。ベースメソッドを呼び出す「唯一の」方法は、サブクラスから、base.MyMethod(..)を使用することです。

したがって、基本的にルックアップルールは、より深いクラスにある場合はintを受け取るメソッドを選択できますが、メソッドが呼び出されると、オーバーライドされている場合は、子クラスにあるintを受け取るメソッドを呼び出します。

于 2012-05-28T14:58:53.833 に答える
1

2つの記事を読んで、事実を台無しにしていると思います。子クラスにオーバーロードがなく、選択できるものがないため、他の通常のオーバーライドされた関数と同じように動作するはずです。簡単な例を見てみましょう。

public class A
{
  public virtual void Foo()
  {
    Console.WriteLine("A.Foo() called");
  }
}

public class B: A
{
  public override void Foo()
  {
    Console.WriteLine("B.Foo() called");
  }
}

void Main()
{
new B().Foo();
}

期待される出力はどれくらいですか?明らかに、二度と考えずに、誰もがB.Foo()が呼び出されたと言うつもりです。あなたの場合、まったく同じことが起こっています。基本クラスがより優れたオーバーロードされたメソッドを持っているかどうかは関係ありません。子クラスがそれを勝ち取るだけです。物事を過度に複雑にしないでください。

ケース3については、これについては完全にはわかりませんが、ここで発生するのは、コンパイラがオブジェクトへの暗黙の変換を試行し、同じシグネチャを持つより深いメソッドが存在することを検出したためです。

階層の異なるレベルに2つのメソッドがある場合、呼び出しの「より優れた関数メンバー」でなくても、「より深い」メソッドが最初に選択されます。

そして今、それが基本クラスに行くと、使用できるより良いメソッドがあることに気づき、それ故にそのメソッドを呼び出します。

PS:上記の説明は、観察された結果に基づいており、必ずしも上記の動作の正確な理由ではない可能性があります。

于 2012-05-28T17:36:03.040 に答える