仮想メソッドとは何ですか?


81

メソッドを「仮想」として宣言するのはなぜですか。

仮想を使用する利点は何ですか?

回答:


58

仮想修飾子は、メソッド\プロパティ(ECT)を使用して、派生クラスで変更することができることをマークするために使用されるオーバーライド修飾子。

例:

class A
{
    public virtual void Foo()
       //DoStuff For A
}

class B : A
{
    public override void Foo()
    //DoStuff For B

    //now call the base to do the stuff for A and B 
    //if required
    base.Foo()
}

45

Virtualを使用すると、継承クラスで、基本クラスが使用するメソッドを置き換えることができます。

public class Thingy
{
    public virtual void StepA()
    {
        Console.Out.WriteLine("Zing");
    }

    public void Action()
    {
        StepA();
        Console.Out.WriteLine("A Thingy in Action.");
    }
}

public class Widget : Thingy
{
    public override void StepA()
    {
        Console.Out.WriteLine("Wiggy");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Thingy thingy = new Thingy();
        Widget widget = new Widget();

        thingy.Action();
        widget.Action();

        Console.Out.WriteLine("Press any key to quit.");
        Console.ReadKey();
    }
 }

プログラムを実行すると、出力は次のようになります。

Zing 
A Thingy in Action. 
Wiggy 
A Thingy in Action.

WidgetがThingyレベルで定義されたAction()メソッドを呼び出したにもかかわらず、内部的にThingyがWidgetのStepA()メソッドを呼び出したことに注意してください。

基本的な答えは、クラスの継承者に柔軟性を与えることです。もちろん、クラスをうまく設計する必要があります。そうしないと、大混乱が弱まる可能性があります。


23

仮想メソッドは、実際のメソッド呼び出しが基になるオブジェクトの実行時タイプに依存するメソッドのタイプです。

非仮想メソッドは、呼び出される実際のメソッドが、メソッド呼び出しの時点でのオブジェクトの参照タイプに依存するタイプのメソッドです。


これは答えであるはずです-仮想は宣言の変更条件を介して定義することはできません。それでは、非表示にする方法とどう違うのですか?
SENya

13

MSDNの仮想メソッド

virtualキーワードは、メソッドまたはプロパティの宣言を変更するために使用されます。この場合、メソッドまたはプロパティは仮想メンバーと呼ばれます。仮想メンバーの実装は、派生クラスのメンバーをオーバーライドすることで変更できます。

仮想メソッドが呼び出されると、オブジェクトの実行時型がオーバーライドメンバーについてチェックされます。最も派生したクラスのオーバーライドメンバーが呼び出されます。派生クラスがメンバーをオーバーライドしていない場合は、元のメンバーである可能性があります。(実行時型とほとんどの派生実装の詳細については、10.5.3仮想メソッドを参照してください。)

デフォルトでは、メソッドは非仮想です。非仮想メソッドをオーバーライドすることはできません。

仮想修飾子を次の修飾子と一緒に使用することはできません。

静的 抽象 オーバーライド

仮想プロパティは、宣言と呼び出し構文の違いを除いて、抽象メソッドのように動作します。

  • 静的プロパティで仮想修飾子を使用するとエラーになります。
  • 仮想継承プロパティは、オーバーライド修飾子を使用するプロパティ宣言を含めることにより、派生クラスでオーバーライドできます。

6

クラスから派生する予定がない場合でも、クラスをモックするためにメソッドを仮想としてマークする必要がある場合があります。一部のモックフレームワークでは、仮想メソッドのモックのみが許可されます。インターフェイスを実装するメソッドは暗黙的に仮想であることに注意してください。

私はこの制限があるRhinoMocksを使用しており、この理由だけでデフォルトでメソッドを仮想としてマークしています。私にとって、これはおそらく、継承が機能するケースがはるかに少ないため、仮想メソッドを使用する最大の理由です。


5

仮想メソッドは、派生クラスへの実装がオプションであることを除いて、基本クラスの抽象メソッドに似ています。また、仮想メソッドにロジックを配置し、派生クラスでこれらをオーバーライドすることもできます。


4

短い質問、短い答え!メソッドが属するクラスを継承すると思われる場合は、メソッドを「仮想」として認定してください。

より長い答え:「仮想を使用すると、オーバーライドして、派生クラスのメソッドに別の意味を与えることができます。


3

継承クラスでそれをオーバーライドできるようにするため。

キーワードのMSDNエントリを確認してください。それはそれをより深く説明します。


1

言うまでもなく、仮想メソッドは、コードがOpen ClosedPrincipleに準拠しようとしているときに役立ちます。

オープンクローズド原則の詳細については、こちらをご覧ください、ボブおじさんのオリジナルのOCPホワイトペーパーを。

また、Javaとは異なり、C#ではメソッドがデフォルトで仮想ではないことに注意してください。



1

仮想関数は、実際には存在しない関数です。派生クラスは、仮想関数をオーバーライドすることで変更できます。仮想関数は、実行時のポリモーフィズムを実現する方法の1つです。

    public class sample {
      public virtual void fun(){
        Console.WriteLine("base sample class \n");
      }
    }
    public class A : sample{
      public override void fun(){
        Console.WriteLine("Class A \n");
      }
    }
    public class B : sample{
      public override void fun(){
        Console.WriteLine("Class B \n");
      }
    }
    class run{
      public static void main(String[] args){
        sample obj = new sample();
        sample obj1 = new A();
        sample obj2 = new B();
        obj.fun();
        obj1.fun();
        obj2.fun();
      }
    }

「実際には存在しない」とはどういう意味ですか?参考資料を提供して
いただけ

これはC#の継承のようには見えません。public後の アクセス修飾子はclass Aclass Bコンパイル時エラーを引き起こします。派生クラスからの基本クラスのメンバーのアクセス可能性は、基本クラスから個別に指定されます(デフォルトではメンバーはですprivate)。
ミントラン2017年

@Minh Tran-はい、その通りです。それはc ++の継承でした。とにかく、私は投稿を編集しました。
Lineesh K Mohan 2017

1

ランタイムはコンパイル時に発生します。
メソッドを仮想として宣言する場合、派生クラスで宣言するには、overrideまたはnew修飾子を追加する必要があります。
いつそれを見ることができますTrySpeak。子と父親を渡すと、どちらもSpeak of FatherをTryScream呼び出しますが、はそれぞれのメソッドを呼び出します。
これを理解するために、知っておくべきことがいくつかあります。ChildのインスタンスではScream、ChildクラスまたはFatherクラスの2つのメソッドがあります。Scream fromChildクラスまたはFatherクラスのいずれかを呼び出すことができます。のでVirtaulそれも手段派生クラスでオーバーライドすることができますので、修飾子は、メソッドをマークScream父クラスから呼び出され、それはあなたが新しい修飾子を使用している場合、それはdefferentになり、上書きされます。

import system;
class Father
{
    Speak()
    {
        Console.Writeline("Father is speaking") 
    }
    virtual Scream()
    {
        Console.Writeline("Father is screaming")    
    }
}
class Child: father
{
    Speak()
    {
        Console.Writeline("Child is speaking")  
    }
    override Scream()
    {
        Console.Writeline("Child is screaming") 
    }
}
class APP
{
    public static void Main()
    {
        // We new two instances here
        Father father = new Father();
        Child child = new Child();        
        // Here we call their scream or speak through TryScream or TrySpeak
        TrySpeak(father);
        TrySpeak(child);
        //>>>"Father is speaking"
        //>>>"Father is speaking"
        TryScream(father);
        TryScream(child);
        //>>>"Father is screaming"
        //>>>"Child is screaming"
    }
    // when your method take an Parameter who type is Father
    // You can either pass in a Father instance or
    // A instance of a derived Class from Father
    // which could be Child
    public static void TrySpeak(Father person)
    {
        person.Scream();
    }
    public static void TryScream(Father person)
    {
        person.Speak();
    }
}

1

C#では、派生クラスの基本クラスメソッドをオーバーライドするには、以下に示すように、基本クラスメソッドを仮想として宣言し、派生クラスメソッドをオーバーライドとして宣言する必要があります。

using System;
namespace Polymorphism
{
 class A
 {
 public virtual void Test() { Console.WriteLine("A::Test()"); }
 }

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

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

 class Program
 {
 static void Main(string[] args)
 {

 A a = new A();
 B b = new B();
 C c = new C();
 a.Test(); // output --> "A::Test()"
 b.Test(); // output --> "B::Test()"
 c.Test(); // output --> "C::Test()"

 a = new B();
 a.Test(); // output --> "B::Test()"

 b = new C();
 b.Test(); // output --> "C::Test()"

 Console.ReadKey();
 }
 }
}

派生クラスのメソッドは仮想と新規を同時に使用できるため、virtualキーワードとnewキーワードを使用して、メソッドの非表示とオーバーライドを混在させることもできます。これは、以下に示すように、クラスCのクラスB、Test()メソッドをオーバーライドしているため、派生クラスメソッドを次のレベルにさらにオーバーライドする場合に必要です。

using System;
namespace Polymorphism
{
 class A
 {
 public void Test() { Console.WriteLine("A::Test()"); }
 }

 class B : A
 {
 public new virtual void Test() { Console.WriteLine("B::Test()"); }
 }

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

 class Program
 {
 static void Main(string[] args)
 {

 A a = new A();
 B b = new B();
 C c = new C();

 a.Test(); // output --> "A::Test()"
 b.Test(); // output --> "B::Test()"
 c.Test(); // output --> "C::Test()"

 a = new B();
 a.Test(); // output --> "A::Test()"

 b = new C();
 b.Test(); // output --> "C::Test()"

 Console.ReadKey();
 }
 }
}

GOLDEN WORDS:virtualキーワードは、基本クラスで宣言されたメソッド、プロパティ、インデクサー、またはイベントを変更し、派生クラスでオーバーライドできるようにするために使用されます。

overrideキーワードは、基本クラスの仮想/抽象メソッド、プロパティ、インデクサー、またはイベントを派生クラスに拡張または変更するために使用されます。

newキーワードは、基本クラスのメソッド、プロパティ、インデクサー、またはイベントを派生クラスに隠すために使用されます。

楽しい :-)


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.