Вызов переопределенного метода из базового класса в C # - PullRequest
19 голосов
/ 19 февраля 2010

Учитывая следующие определения и код класса 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.
    }
}

Я хотел бы вызвать MyMethod (), который фактически находится в A или B, предполагая, что переданный объект на самом деле является экземпляром Aили B, а не то, что находится в BaseClass.Если не делать что-то вроде этого:


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

Как я могу это сделать?

Ответы [ 5 ]

14 голосов
/ 19 февраля 2010

Какой метод вызывается, определяется с помощью полиморфизма типа, который передается в конструктор 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() 
9 голосов
/ 19 февраля 2010

Извините, но вы полностью ошибаетесь;это пошло бы против всей сути виртуальных методов.Если someObject является A, то будет вызываться A.MyMethod.Если someObject является B, то будет вызываться B.MyMethod.Если someObject является BaseClass и не является экземпляром типа, производного от 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 (Виртуальные методы)

При вызове виртуального метода тип времени выполнения экземпляра, для которого выполняется этот вызов, определяетфактическая реализация метода для вызова.

(жирный и курсив в оригинале.)

В точном выражении, когда метод с именем N вызывается со списком аргументов A в экземпляре стип времени компиляции C и тип времени выполнения R (где R равен либо C или классу, производному от C), вызов обрабатывается следующим образом:

• Сначала разрешение перегрузки применяется к C, N и A, чтобы выбрать конкретный метод M из набора методов, объявленных и унаследованных C.Это описано в §7.5.5.1.

• Затем, если M является не виртуальным методом, вызывается M.

В противном случае, Mявляется виртуальным методом, и вызывается наиболее производная реализация M по отношению к R.

(жирный шрифт не в оригинале.)

Затем нам нужноопределение «наиболее производная реализация M».Это хорошее рекурсивное определение:

Наиболее производная реализация виртуального метода M относительно класса R определяется следующим образом:

• Если R содержит вводное виртуальное объявление M, то это наиболее производная реализация M.

• В противном случае, если R содержит переопределение M, то это наиболее производноереализация M.

• В остальном, наиболее производная реализация M относительно R совпадает с самой производной реализацией M относительно прямого базового класса R.

Таким образом, в нашем примере выше с Cat : Animal и Dog : Animal, когда параметр a до Feeder.Feed(Animal) является экземпляром Cat, тогда Cat.Speak являетсянаиболее производная реализация.Вот почему мы увидим «Meow!», а не «i can haz cheezburger?»

2 голосов
/ 19 февраля 2010

Если 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");
    }
}
1 голос
/ 19 февраля 2010

Поскольку вы ввели его как BaseClass вместо A или B, базовый класс является начальной точкой для вызовов метода.

Вы можете попробовать использовать универсальный:

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

Я не уверен, насколько хорошо это будет выглядеть в конструкторе, но вы можете переместить это в другой метод.

1 голос
/ 19 февраля 2010

Если некоторый переданный объект является классом A, то вызывается A.MyMethod, а не реализация базового класса. Также посмотрите на ключевое слово is .

...