Вызов переопределенного метода из базового класса в С#
Учитывая следующие определения и код класса С#:
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(), который фактически найден в или 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();
}
}
}
Как я могу это сделать?
Ответы
Ответ 1
Какой метод вызывается, определяется с помощью полиморфизма по типу, который передается в конструктор 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()
Ответ 2
Извините, но вы полностью ошибаетесь; это будет противоречить всей точке виртуальных методов. Если 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?
"
Ответ 3
Если 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");
}
}
Ответ 4
Если someObject передан в классе A, тогда вызывается A.MyMethod, а не реализация базового класса. Также посмотрите ключевое слово.
Ответ 5
Поскольку вы набрали его как BaseClass вместо A или B, базовый класс является отправной точкой для вызовов методов.
Вы можете попробовать использовать общий:
public class AnotherObject
{
public AnotherObject<T>(T someObject) where T : BaseClass
{
someObject.MyMethod(); //This calls the BaseClass method, unfortunately.
}
}
Я не уверен, насколько хорошо это будет выполняться в конструкторе, но вы можете переместить это на другой метод.