web-dev-qa-db-ja.com

C#の基本クラスからオーバーライドされたメソッドを呼び出す

次のC#クラス定義とコードがあるとします。


public class BaseClass
{
    public virtual void MyMethod()
    {
        ...do something...
    }
}

public class A : BaseClass
{
    public override void MyMethod()
    {
        ...do something different...
    }
}

public class B : BaseClass
{
    public override void MyMethod()
    {
        ...do something different...
    }
}

public class AnotherObject
{
    public AnotherObject(BaseClass someObject)
    {
        someObject.MyMethod(); //This calls the BaseClass method, unfortunately.
    }
}

渡されたオブジェクトが実際にはBaseClassにあるものではなく、AまたはBのインスタンスであると仮定して、実際にAまたはBにあるMyMethod()を呼び出したいと思います。このようなことをするのに足りない:


public class AnotherObject
{
    public AnotherObject(BaseClass someObject)
    {
        A temp1 = someObject as A;
        if (A != null)
        {
            A.MyMethod();
        }

        B temp2 = someObject as B;
        if (B != null)
        {
            B.MyMethod();
        }
    }
}

どうすればいいですか?

19
David

どのメソッドが呼び出されるかは、AnotherObjectコンストラクターに渡される型の多形によって決定されます。

AnotherObject a = new AnotherObject(new A()); // invokes A.MyMethod() 
AnotherObject b = new AnotherObject(new B()); // invokes B.MyMethod() 
AnotherObject c = new AnotherObject(new BaseClass()); //invokes BaseClass.MyMethod() 
14
JonathanK

申し訳ありませんが、あなたは完全に間違っています。これは、仮想メソッドの全体的なポイントに反します。 someObjectAの場合、_A.MyMethod_が呼び出されます。 someObjectBの場合、_B.MyMethod_が呼び出されます。 someObjectBaseClassであり、BaseClassから派生したタイプのインスタンスではない場合、_BaseClass.MyMethod_が呼び出されます。

みんなのお気に入りの例を使ってみましょう:

_class Animal {
    public virtual void Speak() {
        Console.WriteLine("i can haz cheezburger?");
    } 
}
class Feeder {
    public void Feed(Animal animal) { animal.Speak(); }
}
class Cat : Animal {
    public override void Speak() { Console.WriteLine("Meow!"); }
}
class Dog : Animal {
    public override void Speak() { Console.WriteLine("Woof!"); }
}
_

次に:

_Animal a = new Animal();
Animal c = new Cat();
Animal d = new Dog();
Feeder f = new Feeder();
f.Feed(a);
f.Feed(c);
f.Feed(d);
_

これは印刷されます:

_i can haz cheezburger?
Meow!
Woof!
_

繰り返しますが、これが仮想メソッドの要点です。

さらに、仕様に行くことができます。 10.6.3から(仮想メソッド)

仮想メソッドの呼び出しでは、その呼び出しが行われるインスタンスのrun-time typeが実際のメソッドの実装を決定します呼び出す。

(元の太字と斜体。)

正確に言うと、Nという名前のメソッドがコンパイル時タイプAおよび実行時タイプCRRまたはCから派生したクラス)のインスタンスで引数リストCを使用して呼び出された場合次のように処理されます。

•最初に、過負荷解決がCN、およびAに適用され、Mで宣言され、継承される一連のメソッドから特定のメソッドCを選択します。これは§7.5.5.1で説明されています。

•次に、Mが非仮想メソッドの場合、Mが呼び出されます。

それ以外の場合、Mは仮想メソッドであり、Rに関してMの最も派生した実装が呼び出されます。

(太字はオリジナルではありません。)

次に、「Mの最も派生した実装」の定義が必要です。これは素晴らしい再帰的定義です:

クラスMに関する仮想メソッドRの最も派生した実装は、次のように決定されます。

RMの導入仮想宣言が含まれている場合、これはMの最も派生した実装です。

•それ以外の場合、RMのオーバーライドが含まれている場合、これはMの最も派生した実装です。

•それ以外の場合、Mに関するRの最も派生した実装は、Mの直接基本クラスに関するRの最も派生した実装と同じです。

したがって、上記の_Cat : Animal_および_Dog : Animal_の例では、Feeder.Feed(Animal)へのパラメーターaCatのインスタンスである場合、_Cat.Speak_が最も派生した実装です。これが、「_Meow!_」ではなく「_i can haz cheezburger?_」が表示される理由です。

9
jason

MyMethod()が基本クラスで抽象である場合、派生クラスのバージョンが使用されます。したがって、基本クラスのインスタンスを呼び出す必要がない場合は、これがオプションになります。

    static void Main(string[] args)
    {

        A classA = new A();
        B classB = new B();

        DoFunctionInClass(classA);
        DoFunctionInClass(classB);
        DoFunctionInClass(classA as BaseClass);

        Console.ReadKey();
    }

    public static void DoFunctionInClass(BaseClass c) 
    {
        c.MyMethod();
    }



public abstract class BaseClass
{
    public abstract void MyMethod();
}


public class A : BaseClass
{
    public override void MyMethod()
    {
        Console.WriteLine("Class A");
    }
}

public class B : BaseClass
{
    public override void MyMethod()
    {
        Console.WriteLine("Class B");
    }
}
2
slugster

AまたはBではなくBaseClassとして入力したため、baseclassはメソッド呼び出しの開始点です。

ジェネリックを使用してみてください。

public class AnotherObject 
{ 
    public AnotherObject<T>(T someObject) where T : BaseClass
    { 
        someObject.MyMethod(); //This calls the BaseClass method, unfortunately. 
    } 
} 

これがコンストラクターでどれだけうまくいくかはわかりませんが、これを別のメソッドに移動できる可能性があります。

1
Joel Etherton

渡されたsomeObjectがクラスAの場合、基本クラスの実装ではなく、A.MyMethodが呼び出されます。 is キーワードも見てください。

1