Подтвердить что ты не робот

Вызов переопределенного метода из базового класса в С#

Учитывая следующие определения и код класса С#:


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

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

4b9b3361

Ответ 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. 
    } 
} 

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