web-dev-qa-db-ja.com

派生クラスのメソッドを呼び出すと、基本クラスのメソッドが呼び出されるのはなぜですか?

次のコードを検討してください。

class Program
{
    static void Main(string[] args)
    {
        Person person = new Teacher();
        person.ShowInfo();
        Console.ReadLine();
    }
}

public class Person
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public new void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

このコードを実行すると、次が出力されます。

私は人です

ただし、Teacherではなく、Personのインスタンスであることがわかります。なぜコードはそれを行うのですか?

143
user1968030

newvirtual/overrideには違いがあります。

クラスは、インスタンス化されたとき、そのメソッドの実際の実装を指すポインターのテーブルにすぎないことを想像できます。次の画像は、これをかなりよく視覚化するはずです。

Illustration of method implementations

現在、さまざまな方法があり、メソッドを定義できます。継承で使用される場合、それぞれの動作は異なります。標準的な方法は、常に上の画像が示すように機能します。この動作を変更する場合は、メソッドに異なるキーワードを添付できます。

1.抽象クラス

最初のものはabstractです。 abstractメソッドは、単にnowhereを指します。

Illustration of abstract classes

クラスに抽象メンバーが含まれる場合は、abstractとしてマークする必要もあります。そうでない場合、コンパイラーはアプリケーションをコンパイルしません。 abstractクラスのインスタンスを作成することはできませんが、それらから継承して、継承されたクラスのインスタンスを作成し、基本クラス定義を使用してそれらにアクセスできます。あなたの例では、これは次のようになります。

public abstract class Person
{
    public abstract void ShowInfo();
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

public class Student : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a student!");
    }
}

呼び出された場合、ShowInfoの動作は実装に基づいて異なります。

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a student!'

StudentsとTeachersは両方ともPersonsですが、自分自身に関する情報を要求するように求められた場合、動作が異なります。ただし、情報を要求する方法は同じです。Personクラスインターフェイスを使用します。

それでは、Personを継承すると、舞台裏で何が起こりますか? ShowInfoを実装するとき、ポインターはnowhereを指していないため、実際の実装を指しています! Studentインスタンスを作成する場合、Students ShowInfoを指します。

Illustration of inherited methods

2.仮想メソッド

2番目の方法は、virtualメソッドを使用することです。基本クラスでoptionalデフォルト実装を提供していることを除いて、動作は同じです。 virtualメンバーを持つクラスはインスタンス化できますが、継承されたクラスは異なる実装を提供できます。コードは実際に動作するように見えるはずです:

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am a person!");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

主な違いは、ベースメンバPerson.ShowInfonowhereを指していないことです。これは、Personのインスタンスを作成できる理由でもあります(したがって、abstractとしてマークする必要がなくなりました)。

Illustration of a virtual member inside a base class

現時点では、これは最初の画像と変わらないことに注意してください。これは、virtualメソッドが「標準的な方法」の実装を指しているためです。 virtualを使用すると、canmustではなく)がPersonsの異なる実装を提供することをShowInfoに伝えることができます。上記のoverrideで行ったように、(Teacherを使用して)別の実装を提供する場合、イメージはabstractの場合と同じになります。 Studentsのカスタム実装を提供しなかったことを想像してください。

public class Student : Person
{
}

コードは次のように呼び出されます。

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a person!'

そして、Studentの画像は次のようになります。

Illustration of the default implementation of a method, using virtual-keyword

3.魔法の「新しい」キーワード、別名「シャドウイング」

newは、これに関するハックです。基本クラス/インターフェースのメソッドと同じ名前を持つ一般化クラスのメソッドを提供できます。どちらも独自のカスタム実装を指します:

Illustration of the "way around" using the new-keyword

実装は、提供されたもののように見えます。メソッドへのアクセス方法に基づいて、動作は異なります。

Teacher teacher = new Teacher();
Person person = (Person)teacher;

teacher.ShowInfo();    // Prints 'I am a teacher!'
person.ShowInfo();     // Prints 'I am a person!'

この動作は望ましい場合がありますが、あなたの場合は誤解を招きます。

これにより、理解しやすくなることを願っています!

364
Carsten

C#のサブタイプ多型は、C++と似ていますがJavaとは異なり、明示的な仮想性を使用します。これは、メソッドをオーバーライド可能として明示的にマークする必要があることを意味します(つまり、virtual)。 C#では、タイプミスを防ぐために、オーバーライドするメソッドをオーバーライドするように明示的にマークする必要があります(つまり、override)。

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

質問のコードでは、newを使用します。これは、オーバーライドする代わりに shadowing を実行します。シャドウイングは、実行時のセマンティクスではなく、コンパイル時のセマンティクスにのみ影響するため、意図しない出力になります。

45
user142019

メソッドを作成する必要があります virtual そして、親クラス参照に配置したクラスオブジェクトのメソッドを呼び出すために、子クラスの関数をオーバーライドする必要があります。

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

仮想メソッド

仮想メソッドが呼び出されると、オブジェクトの実行時の型がオーバーライドするメンバーについてチェックされます。最も派生したクラスのオーバーライドするメンバーが呼び出されます。派生したクラスがメンバーをオーバーライドしていない場合は、元のメンバーである可能性があります。デフォルトでは、メソッドは非仮想です。非仮想メソッドをオーバーライドすることはできません。 virtual、modifier、static、abstract、private、またはoverride修飾子 MSDN を使用することはできません。

シャドウイングにNewを使用する

オーバーライドの代わりに新しいキーWordを使用しています。これがnewの機能です

  • 派生クラスのメソッドの前に新しいキーワードまたはオーバーライドキーワードがない場合、コンパイラは警告を発行し、メソッドは新しいキーワードが存在するかのように動作します。

  • 派生クラスのメソッドの前に新しいキーワードが付いている場合、メソッドは基本クラスのメソッドとは独立していると定義されます、This- MSDNの記事 は非常によく説明しています。

早期バインディングVS遅延バインディング

通常のメソッド(仮想ではない)のコンパイル時に早期バインディングがあります。これは、コンパイラが呼び出しを、参照型のメソッド(ベースクラス)ではなくベースクラスのメソッドにバインドする現在のケースですオブジェクトは、ベースクラスの参照、つまり派生クラスオブジェクトに保持されます。これは、ShowInfoが仮想メソッドではないためです。遅延バインディングは、 仮想メソッドテーブル (vtable)を使用して(仮想/オーバーライドされたメソッド)の実行時に実行されます。

通常の関数の場合、コンパイラはメモリ内の数値の位置を計算できます。次に、関数が呼び出されると、このアドレスで関数を呼び出す命令を生成できます。

仮想メソッドを持つオブジェクトの場合、コンパイラはvテーブルを生成します。これは基本的に、仮想メソッドのアドレスを含む配列です。仮想メソッドを持つすべてのオブジェクトには、vテーブルのアドレスであるコンパイラーによって生成された隠しメンバーが含まれます。仮想関数が呼び出されると、コンパイラはvテーブル内の適切なメソッドの位置を特定します。次に、オブジェクトv-tableを調べるコードを生成し、この位置で仮想メソッドを呼び出します Reference

25
Adil

Achratt's answer から構築したい。完全を期すため、違いは、OPが派生クラスのメソッドのnewキーワードが基本クラスのメソッドをオーバーライドすることを期待していることです。実際に行うのは、基本クラスメソッドhideです。

C#では、別の答えとして述べたように、従来のメソッドのオーバーライドは明示的でなければなりません。基本クラスメソッドはvirtualとしてマークされ、派生クラスは具体的には基本クラスメソッドoverrideである必要があります。これが行われる場合、オブジェクトが基本クラスまたは派生クラスのインスタンスとして扱われるかどうかは関係ありません。派生メソッドが見つかり、呼び出されます。これは、C++と同様の方法で行われます。コンパイル時に「仮想」または「オーバーライド」とマークされたメソッドは、参照オブジェクトの実際のタイプを判別し、変数タイプから実際のオブジェクトタイプまでツリーに沿ってオブジェクト階層を下に移動することにより(実行時に)解決されます。変数タイプによって定義されたメソッドの最も派生した実装を見つけます。

これは、「暗黙的なオーバーライド」を許可するJavaとは異なります。インスタンスメソッド(非静的)の場合、同じシグネチャ(パラメータの名前と番号/タイプ)のメソッドを定義するだけで、サブクラスがスーパークラスをオーバーライドします。

制御しない非仮想メソッドの機能を拡張またはオーバーライドすると便利なことが多いため、C#にはnewコンテキストキーワードも含まれています。 newキーワードは、親メソッドをオーバーライドする代わりに「非表示」にします。継承可能なメソッドは、仮想であるかどうかにかかわらず非表示にできます。これにより、開発者は、親から継承したいメンバーを活用できます。そうしないと、コードのコンシューマーに同じ「インターフェース」を提示できます。

非表示は、非表示メソッドが定義されている継承レベル以下でオブジェクトを使用している人の観点からオーバーライドするのと同様に機能します。質問の例から、コーダーは教師を作成し、その参照を教師タイプの変数に保存することで、教師からShowInfo()実装の動作を見ることができます。ただし、Personレコードのコレクション内のオブジェクトを操作しているユーザーは、ShowInfo()のPerson実装の動作を確認できます。 Teacherのメソッドはその親をオーバーライドしないため(Person.ShowInfo()も仮想である必要があります)、抽象化のPersonレベルで動作するコードはTeacher実装を見つけられず、使用しません。

さらに、newキーワードがこれを明示的に行うだけでなく、C#は暗黙的なメソッドの非表示を許可します。 overrideまたはnewを使用せずに、親クラスメソッドと同じシグネチャでメソッドを定義するだけで、非表示になります(ただし、コンパイラ警告またはReSharperやCodeRushなどの特定のリファクタリングアシスタントから苦情が発生します) 。これは、C#の設計者がC++の明示的なオーバーライドとJavaの暗黙的なオーバーライドとの間で考え出した妥協案です。エレガントではありますが、古い言語のいずれかのバックグラウンドから来た場合に期待する動作を常に生成するわけではありません。

ここに新しいものがあります:これは、2つのキーワードを長い継承チェーンで組み合わせると複雑になります。以下を考慮してください。

class Foo { public virtual void DoFoo() { Console.WriteLine("Foo"); } }
class Bar:Foo { public override sealed void DoFoo() { Console.WriteLine("Bar"); } }
class Baz:Bar { public virtual void DoFoo() { Console.WriteLine("Baz"); } }
class Bai:Baz { public override void DoFoo() { Console.WriteLine("Bai"); } }
class Bat:Bai { public new void DoFoo() { Console.WriteLine("Bat"); } }
class Bak:Bat { }

Foo foo = new Foo();
Bar bar = new Bar();
Baz baz = new Baz();
Bai bai = new Bai();
Bat bat = new Bat();

foo.DoFoo();
bar.DoFoo();
baz.DoFoo();
bai.DoFoo();
bat.DoFoo();

Console.WriteLine("---");

Foo foo2 = bar;
Bar bar2 = baz;
Baz baz2 = bai;
Bai bai2 = bat;
Bat bat2 = new Bak();

foo2.DoFoo();
bar2.DoFoo();
baz2.DoFoo();
bai2.DoFoo();    

Console.WriteLine("---");

Foo foo3 = bak;
Bar bar3 = bak;
Baz baz3 = bak;
Bai bai3 = bak;
Bat bat3 = bak;

foo3.DoFoo();
bar3.DoFoo();
baz3.DoFoo();
bai3.DoFoo();    
bat3.DoFoo();

出力:

Foo
Bar
Baz
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat

最初の5つのセットはすべて予想されるものです。各レベルには実装があり、インスタンス化されたのと同じタイプのオブジェクトとして参照されるため、ランタイムは各呼び出しを変数タイプによって参照される継承レベルに解決します。

5番目のセットは、各インスタンスを直接の親タイプの変数に割り当てた結果です。現在、振る舞いのいくつかの違いが揺らいでいます。 foo2は、実際にはBarとしてキャストされたFooですが、実際のオブジェクトタイプBarのより派生したメソッドを引き続き検索します。 bar2Bazですが、foo2とは異なり、BazはBarの実装を明示的にオーバーライドしないため(できません; Bar sealed it)、見られません「トップダウン」で見たときのランタイム。したがって、Barの実装が代わりに呼び出されます。 Bazはnewキーワードを使用する必要がないことに注意してください。キーワードを省略するとコンパイラの警告が表示されますが、C#の暗黙の動作は親メソッドを非表示にすることです。 baz2Baiであり、Baznew実装をオーバーライドします。したがって、その動作はfoo2に似ています。 Baiでの実際のオブジェクトタイプの実装が呼び出されます。 bai2Batであり、その親Baiのメソッド実装を再び非表示にし、Baiの実装がシールされていない場合でも、bar2と同じように動作します。メソッドを非表示にする代わりにオーバーライドしました。最後に、bat2Bakであり、どちらの種類のオーバーライド実装も持たず、単に親の実装を使用します。

5つ目の3番目のセットは、完全なトップダウン解像度の動作を示しています。実際には、チェーン内の最も派生したクラスのインスタンスBakを実際に参照していますが、変数チェーンのすべてのレベルでの解決は、継承チェーンのそのレベルで開始し、最も派生したexplicitメソッドのオーバーライド。これは、BarBai、およびBatにあります。したがって、メソッドの非表示は、オーバーライドする継承チェーンを「破壊」します。非表示メソッドを使用するには、メソッドを非表示にする継承レベル以下でオブジェクトを操作する必要があります。それ以外の場合、hiddenメソッドは「発見」され、代わりに使用されます。

7
KeithS

C#のポリモーフィズムについてお読みください: ポリモーフィズム(C#プログラミングガイド)

これはそこからの例です:

新しいキーワードを使用すると、置き換えられた基本クラスメンバーの代わりに、新しいクラスメンバーが呼び出されます。これらの基本クラスメンバーは、非表示メンバーと呼ばれます。派生クラスのインスタンスが基本クラスのインスタンスにキャストされる場合、非表示のクラスメンバーを呼び出すことができます。例えば:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.
4
Emil

virtualにしてから、Teacherでその関数をオーバーライドする必要があります。派生クラスを参照するためにベースポインターを継承および使用しているため、virtualを使用してオーバーライドする必要があります。 newは、baseクラス参照ではなく、派生クラス参照でbaseクラスメソッドを非表示にするためのものです。

3
Jay Patel

この周辺の情報を拡張するために、いくつかの例を追加したいと思います。これも役立つことを願っています:

これは、派生型が基本型に割り当てられたときに何が起こるかを明確にするコードサンプルです。このコンテキストで使用可能なメソッドと、オーバーライドされたメソッドと隠されたメソッドの違い。

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.foo();        // A.foo()
            a.foo2();       // A.foo2()

            a = new B();    
            a.foo();        // B.foo()
            a.foo2();       // A.foo2()
            //a.novel() is not available here

            a = new C();
            a.foo();        // C.foo()
            a.foo2();       // A.foo2()

            B b1 = (B)a;    
            b1.foo();       // C.foo()
            b1.foo2();      // B.foo2()
            b1.novel();     // B.novel()

            Console.ReadLine();
        }
    }


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

        public void foo2()
        {
            Console.WriteLine("A.foo2()");
        }
    }

    class B : A
    {
        public override void foo()
        {
            // This is an override
            Console.WriteLine("B.foo()");
        }

        public new void foo2()      // Using the 'new' keyword doesn't make a difference
        {
            Console.WriteLine("B.foo2()");
        }

        public void novel()
        {
            Console.WriteLine("B.novel()");
        }
    }

    class C : B
    {
        public override void foo()
        {
            Console.WriteLine("C.foo()");
        }

        public new void foo2()
        {
            Console.WriteLine("C.foo2()");
        }
    }
}

別の小さな異常は、次のコード行についてです。

A a = new B();    
a.foo(); 

VSコンパイラ(intellisense)は、a.foo()をA.foo()として表示します。

したがって、派生型がベース型に割り当てられると、派生型でオーバーライドされたメソッドが参照されるまで、「ベース型」変数がベース型として機能することは明らかです。これは、隠しメソッドや、親と子のタイプ間で同じ名前の(オーバーライドされない)メソッドを使用すると、少し直感に反する場合があります。

このコードサンプルは、これらの警告を明確にするのに役立ちます。

3
Vaibhav

newキーワードは、現在のクラスのメソッドが、Teacher型の変数に保存されたクラスTeacherのインスタンスがある場合にのみ機能することを示します。または、キャストを使用してトリガーできます:((Teacher)Person).ShowInfo()

2
Miguel

C#は、親/子クラスのオーバーライド動作がJavaと異なります。 Javaのデフォルトでは、すべてのメソッドは仮想であるため、必要な動作はすぐにサポートされます。

C#では、基本クラスでメソッドを仮想としてマークする必要があります。そうすると、必要なものが得られます。

2
Adrian Salazar

遅すぎるかもしれません...しかし、質問は簡単で、答えは同じレベルの複雑さを持つべきです。

コード変数では、PersonはTeacher.ShowInfo()について何も知りません。仮想ではないため、基本クラス参照から最後のメソッドを呼び出す方法はありません。

継承には便利なアプローチがあります-コード階層で何を言いたいか想像してみてください。また、1つまたは別のツールがそれ自体について何を言っているかを想像してみてください。例えば。仮想関数を基本クラスに追加すると、次のようになります。1。デフォルト実装を使用できます。 2.派生クラスで再実装される場合があります。抽象関数を追加すると、サブクラスが実装を作成する必要があるということを意味します。ただし、単純な機能がある場合は、誰もその実装を変更することを期待しないでください。

1

ここの変数 'teacher'の型はtypeof(Person)であり、この型はTeacherクラスについて何も知らず、派生型のメソッドを検索しようとしません。 Teacherクラスのメソッドを呼び出すには、変数をキャストする必要があります:(person as Teacher).ShowInfo()

値型に基づいて特定のメソッドを呼び出すには、基本クラスでキーワード「仮想」を使用し、派生クラスで仮想メソッドをオーバーライドする必要があります。このアプローチにより、仮想メソッドのオーバーライドの有無にかかわらず、派生クラスを実装できます。基本クラスのメソッドは、オーバーライドされたバーチャルのないタイプに対して呼び出されます。

public class Program
{
    private static void Main(string[] args)
    {
        Person teacher = new Teacher();
        teacher.ShowInfo();

        Person incognito = new IncognitoPerson ();
        incognito.ShowInfo();

        Console.ReadLine();
    }
}

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

public class IncognitoPerson : Person
{

}
1
Vi Ki

Keith S.の優れたデモンストレーションと他の全員の質の高い回答に基づいて、さらに完全な完全性のために、明示的なインターフェースの実装を進めて、それがどのように機能するかを示します。以下を考慮してください。

名前空間LinqConsoleApp {

class Program
{

    static void Main(string[] args)
    {


        Person person = new Teacher();
        Console.Write(GetMemberName(() => person) + ": ");
        person.ShowInfo();

        Teacher teacher = new Teacher();
        Console.Write(GetMemberName(() => teacher) + ": ");
        teacher.ShowInfo();

        IPerson person1 = new Teacher();
        Console.Write(GetMemberName(() => person1) + ": ");
        person1.ShowInfo();

        IPerson person2 = (IPerson)teacher;
        Console.Write(GetMemberName(() => person2) + ": ");
        person2.ShowInfo();

        Teacher teacher1 = (Teacher)person1;
        Console.Write(GetMemberName(() => teacher1) + ": ");
        teacher1.ShowInfo();

        Person person4 = new Person();
        Console.Write(GetMemberName(() => person4) + ": ");
        person4.ShowInfo();

        IPerson person3 = new Person();
        Console.Write(GetMemberName(() => person3) + ": ");
        person3.ShowInfo();

        Console.WriteLine();

        Console.ReadLine();

    }

    private static string GetMemberName<T>(Expression<Func<T>> memberExpression)
    {
        MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
        return expressionBody.Member.Name;
    }

}
interface IPerson
{
    void ShowInfo();
}
public class Person : IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person == " + this.GetType());
    }
    void IPerson.ShowInfo()
    {
        Console.WriteLine("I am interface Person == " + this.GetType());
    }
}
public class Teacher : Person, IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Teacher == " + this.GetType());
    }
}

}

出力は次のとおりです。

person:私はPerson == LinqConsoleApp.Teacherです

先生:私は先生です== LinqConsoleApp.Teacher

person1:私は教師です== LinqConsoleApp.Teacher

person2:私は教師です== LinqConsoleApp.Teacher

teacher1:私は先生です== LinqConsoleApp.Teacher

person4:私はPerson == LinqConsoleApp.Personです

person3:私はインターフェイスPerson == LinqConsoleApp.Personです

注意すべき2つのこと:
Teacher.ShowInfo()メソッドは、新しいキーワードを省略します。 newが省略されると、メソッドの動作はnewキーワードが明示的に定義された場合と同じになります。

オーバーライドキーワードは、仮想キーWordと組み合わせてのみ使用できます。基本クラスメソッドは仮想でなければなりません。または、抽象の場合、クラスも抽象でなければなりません。

teacherクラスは基本実装(仮想宣言なし)をオーバーライドできず、personは.GetType(Teacher)であるため、PersonはShowInfoの基本実装を取得し、Teacherクラスの実装を非表示にします。

teacherはTypeof(Teacher)であり、Person継承レベルではないため、showInfoの派生したTeacher実装を取得します。

person1は.GetType(Teacher)であり、暗黙の新しいキーワードが基本実装を隠すため、派生したTeacher実装を取得します。

person2は、IPersonを実装し、IPersonへの明示的なキャストを取得しても、派生したTeacher実装も取得します。これは、TeacherクラスがIPerson.ShowInfo()メソッドを明示的に実装していないためです。

teacher1は.GetType(Teacher)であるため、派生したTeacher実装も取得します。

Personクラスのみが明示的にメソッドを実装し、person3がIPerson型のインスタンスであるため、person3のみがShowInfoのIPerson実装を取得します。

インターフェイスを明示的に実装するには、ターゲットインターフェイスタイプのvarインスタンスを宣言する必要があり、クラスはインターフェイスメンバを明示的に実装(完全修飾)する必要があります。

Person4でさえIPerson.ShowInfo実装を取得していないことに注意してください。これは、person4が.GetType(Person)であり、PersonがIPersonを実装している場合でも、person4はIPersonのインスタンスではないためです。

0
steely

簡単な答えを出したかっただけです-

オーバーライドできるクラスでは、virtualおよびoverrideを使用する必要があります。子クラスによってオーバーライドできるメソッドにはvirtualを使用し、そのようなoverrideメソッドをオーバーライドする必要があるメソッドにはvirtualを使用します。

0
Shruti Kapoor

コンパイラは、Teacherであることを知らないため、これを行います。知っているのは、それがPersonまたはそれから派生したものであることだけです。したがって、できることはPerson.ShowInfo()メソッドを呼び出すことだけです。

0
Cole Johnson

いくつかの変更を除いて、上記のJavaで述べたのと同じコードを書きましたが、例外としては正常に機能しました。基本クラスのメソッドはオーバーライドされるため、表示される出力は「I am Teacher」です。

理由:派生クラスの参照を実際に含む基本クラス(派生クラスのインスタンスを参照できる)の参照を作成しているため。また、インスタンスは、最初にメソッドを見つけた場合は常に最初にそのメソッドを参照し、定義を見つけられなかった場合は階層内で上に移動することを知っています。

public class inheritance{

    public static void main(String[] args){

        Person person = new Teacher();
        person.ShowInfo();
    }
}

class Person{

    public void ShowInfo(){
        System.out.println("I am Person");
    }
}

class Teacher extends Person{

    public void ShowInfo(){
        System.out.println("I am Teacher");
    }
}
0
Sanjeev Chauhan

盲目的に起動し、コードの重複を減らすためのLinQPadサンプル.

void Main()
{
    IEngineAction Test1 = new Test1Action();
    IEngineAction Test2 = new Test2Action();
    Test1.Execute("Test1");
    Test2.Execute("Test2");
}

public interface IEngineAction
{
    void Execute(string Parameter);
}

public abstract class EngineAction : IEngineAction
{
    protected abstract void PerformAction();
    protected string ForChildren;
    public void Execute(string Parameter)
    {  // Pretend this method encapsulates a 
       // lot of code you don't want to duplicate 
      ForChildren = Parameter;
      PerformAction();
    }
}

public class Test1Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Performed: " + ForChildren).Dump();
    }
}

public class Test2Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Actioned: " + ForChildren).Dump();
    }
}
0
Yrd