Вызов переопределенного метода от базового класса в 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.
    }
}

Я хотел бы назвать 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();
        }
    }
}

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

19
задан Cœur 16 January 2017 в 16:19
поделиться

5 ответов

Какой метод будет вызван, определяется через полиморфизм типа, передаваемого в конструктор 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
ответ дан 30 November 2019 в 04:20
поделиться

Извините, но вы совершенно ошибаетесь; это противоречит всему смыслу виртуальных методов. Если 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 является наиболее производной реализацией. Вот почему мы увидим "Мяу! ", а не "i can haz cheezburger?"

.
9
ответ дан 30 November 2019 в 04:20
поделиться

Если 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
ответ дан 30 November 2019 в 04:20
поделиться

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

1
ответ дан 30 November 2019 в 04:20
поделиться

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

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

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

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

1
ответ дан 30 November 2019 в 04:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: