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


146

このコードを考えてみましょう:

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。なぜコードはそれをするのですか?


3
Javaの人からの質問:Console.ReadLine();です。この例に必要ですか?
リッチ

2
@Shahrooz私はあなたの質問に答えることができません-私はC#を知りません。PersonクラスとTeacherクラスでWriteLineを呼び出せるようにするために、メインメソッドでReadLineを呼び出す必要があるかどうかという、非常に簡単なC#の質問をしていました。
リッチ

6
ええ、.NetはMain()が終了すると自動的にコンソールウィンドウを閉じます。これを回避するには、Console.Read()またはConsole.Readline()を使用して追加の入力を待機し、コンソールが開いたままになるようにします。
ケンパチ船長、2013

15
@Richいいえ、そうではありません必要がありますが、多くの場合、このような理由のためにそれが表示されますのVisual Studioからコンソールプログラムを実行するとき、プログラム終了時にコマンドウィンドウがすぐに閉じて、あなたがしたい場合はその参照プログラムの出力を、あなたがそれを指示する必要があります待つ。
AakashM 2013

1
@AakashMありがとう-コンソールはEclipseウィンドウの一部であり、閉じないEclipseに時間を費やしています。それは完全に理にかなっています。
リッチ

回答:


368

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

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

メソッド実装の図

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

1.抽象クラス

最初はabstractです。abstractメソッドは単にどこも指さない:

抽象クラスのイラスト

クラスに抽象メンバーが含まれている場合は、それもとしてマークする必要があり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はどちらもsですがPerson、自分自身についての情報を要求するように求められたときの動作は異なります。ただし、情報の入力を求める方法は同じですPerson。クラスインターフェイスを使用します。

だから、あなたが継承すると、舞台裏で何が起こりますPersonか?を実装するShowInfoと、ポインタはもはやどこも指さなくなり、実際の実装を指すようになります。Studentインスタンスを作成するとき、それはStudentsを指しますShowInfo

継承されたメソッドの図

2.仮想メソッド

2番目の方法は、virtualメソッドを使用することです。基本クラスにオプションのデフォルト実装を提供していることを除いて、動作は同じです。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.ShowInfoがもはやどこも指していないことです。これも理由であり、インスタンスを作成できる理由ですPerson(したがって、これをマークする必要はありabstractません)。

基本クラス内の仮想メンバーのイラスト

今のところ、これは最初の画像と異なっていないことに注意してください。これは、virtualメソッドが「標準的な方法」の実装を指しているためです。を使用すると、に別の実装を提供できる必須でない)とvirtual伝えることができます。上記で私がしたように、異なる実装を(を使用して)提供した場合、イメージはと同じように見えます。sのカスタム実装を提供しなかったと想像してください。PersonsShowInfooverrideTeacherabstractStudent

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は次のようになります。

virtual-keywordを使用した、メソッドのデフォルト実装の図

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

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

new-keywordを使用した「道」のイラスト

実装は、指定したものと似ています。メソッドへのアクセス方法に応じて、動作は異なります。

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

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

この動作は必要な場合がありますが、あなたの場合は誤解を招く可能性があります。

これにより、理解しやすくなります。


9
あなたの素晴らしい答えをありがとう

6
これらの図を生成するために何を使用しましたか?
BlueRaja-Danny Pflughoeft 2013

2
非常に優れた完全な答え。
Nik Bougalis 2013

8
TL; DRあなたが使用new機能の継承を破壊し、スーパークラスの機能の別の新しい関数を作る
ラチェットフリーク

3
@Taymon:実際にはそうではありません...私は、コールが現在でPersonはなくStudent、;)に反対することを明確にしたかっただけです
Carsten

45

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を行うを使用しています。シャドーイングは、実行時のセマンティクスではなく、コンパイル時のセマンティクスに影響するだけなので、意図しない出力になります。


4
OPがそれらの意味を知っていると誰が言うべきか。
コールジョンソン

@ColeJohnson説明を追加します。

25

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

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");
    }
}

仮想メソッド

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

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

あなたはオーバーライドの代わりに新しいキーワードを使用しています、これは新しいことです

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

  • 場合は、派生クラスのメソッドは、新しいキーワードで先行され、メソッドは、基本クラスのメソッドの独立したものとして定義され、このMSDNの記事は非常によく、それを説明しています。

アーリーバインディングVSレイトバインディング

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

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

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


7

Achrattの答えに構築したいと思います。完全性のために、違いは、OPがnew派生クラスのメソッドのキーワードが基本クラスのメソッドをオーバーライドすることを期待していることです。それが実際に行うことは、基本クラスのメソッドを隠すことです。

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

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

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

非表示は、非表示メソッドが定義されている継承レベル以下でオブジェクトを使用する人の観点からオーバーライドするのと同様に機能します。質問の例から、コーダーが教師を作成し、その参照を教師型の変数に格納すると、教師からのShowInfo()実装の動作が確認され、Personからは非表示になります。ただし、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番目の2つ目のセットは、各インスタンスを直接の親タイプの変数に割り当てた結果です。さて、行動のいくつかの違いが揺らぎます。foo2は、実際にはとしてBarキャストされFooますが、実際のオブジェクトタイプBarのより派生したメソッドが見つかります。bar2はですがBaz、とは異なりfoo2、BazはBarの実装を明示的にオーバーライドしないため(それはできません。Baritですsealed)、「トップダウン」で見たときにランタイムからは見えないため、代わりにBarの実装が呼び出されます。Bazはnewキーワードを使用する必要がないことに注意してください。キーワードを省略するとコンパイラ警告が表示されますが、C#での暗黙の動作は親メソッドを非表示にすることです。baz2Bai、これは上書きBazさんnew実装なので、その動作はと似ていfoo2ます。Baiでの実際のオブジェクトタイプの実装が呼び出されます。bai2はでありBat、これも親Baiのメソッド実装を非表示にしbar2ます。Baiの実装がシールされていない場合と同じように動作するため、理論的には、Batがメソッドを非表示にする代わりにオーバーライドした可能性があります。最後にbat2は、Bakどちらの種類のオーバーライド実装も持たず、単にその親の実装を使用します。

5番目の3番目のセットは、完全なトップダウンの解決動作を示しています。すべてが実際にチェーン内の最も派生したクラスのインスタンスを参照していますが、Bak変数タイプのすべてのレベルでの解決は、継承チェーンのそのレベルから開始し、メソッドの最も派生した明示的なオーバーライドにドリルダウンすることで実行されます。ものBarBaiBat。したがって、メソッドを非表示にすると、オーバーライドする継承チェーンが「破壊」されます。非表示メソッドを使用するには、メソッドを非表示にする継承レベル以下のオブジェクトで作業する必要があります。それ以外の場合、隠しメソッドは「カバーされず」、代わりに使用されます。


4

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

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

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

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

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

3

それを作成してvirtualから、その関数をでオーバーライドする必要がありますTeacher。派生クラスを参照するためにベースポインターを継承して使用しているので、を使用してそれをオーバーライドする必要がありますvirtualnew隠蔽するためのものであるbase派生クラスリファレンスとしない上に、クラスメソッドがbaseクラス参照。


3

これに関する情報をさらに詳しく説明するために、さらにいくつかの例を追加したいと思います。これも役立つことを願っています:

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

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()");
        }
    }
}

もう1つの小さな異常は、次のコード行の場合です。

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

VSコンパイラー(インテリセンス)は、a.foo()をA.foo()として表示します。

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

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


2

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

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


2

新しいキーワードTELLその現在のクラスのメソッドはあなたがタイプ教師の変数に格納されたクラスの先生のインスタンスを持っている場合にのみ動作します。または、キャストを使用してトリガーできます:((Teacher)Person).ShowInfo()


1

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

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

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

手遅れになるかもしれません...しかし、質問は単純で、答えは同じレベルの複雑さでなければなりません。

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

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


0

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


0

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

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


0

私はuで前述したのと同じコードを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

キース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キーワードが明示的に定義された場合と同じになります。

overrideキーワードは、virtualキーワードと組み合わせてのみ使用できます。基本クラスメソッドは仮想である必要があります。または抽象クラス。この場合、クラスも抽象でなければなりません。

Teacherクラスは基本実装(仮想宣言なし)をオーバーライドできないため、PersonはShowInfoの基本実装を取得し、Personは.GetType(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

盲目的に起動し、コードの重複を減らすための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();
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.