Полиморфный характер использования Func<T> в C#

Я столкнулся с интересной проблемой на прошлой неделе, и я не уверен, что я действительно понимаю полиморфную природу следующего кода. Я создал этот пример на основе некоторого кода, который я написал.

Базовая настройка

  • У меня есть класс "Дрессировщик", который умеет дрессировать животное.
  • Я использую это в качестве базового класса и создал подкласс "DogTrainer", который умеет тренировать только собак.
  • Я создал указатель на функцию, используя суперкласс в качестве возвращаемого типа.
  • Затем я вызываю эту функцию, получая новый экземпляр подкласса "DogTrainer".
  • Затем я вызываю метод "Train" экземпляра, возвращенного из указателя функции.
  • Метод "Train" вызывает метод "Trainer" - "Train", а не "DogTrainer" - "Train", как ожидалось

Вот код

// Does not work as expected
// Calls the Trainer Train not the DogTrainer Train
var getTrainer = new Func<Trainer>(() => new DogTrainer(new Dog()));
var funcTrainer = getTrainer();
Console.WriteLine(funcTrainer.Train());

Теперь, если я использую интерфейс в качестве возвращаемого типа, он работает как положено. "ЕСЛИ", то у меня есть интерфейс, непосредственно помеченный в подклассе "DogTrainer".

// Works as expected 
var getITrainer = new Func<ITrainer>(() => new DogTrainer(new Dog()));
var funcITrainer = getITrainer();
Console.WriteLine(funcITrainer.Train());

Если у меня нет интерфейса в подклассе, он не работает должным образом. См пример

// Does not work as expected 
// Calls the Trainer Train not the Dog Trainer Train
var getITrainerWithNoInterface = new Func<ITrainer>(() => new DogTrainerWithNoInterface(new Dog()));
var funcITrainerWithNoInterface = getITrainerWithNoInterface();
Console.WriteLine(funcITrainerWithNoInterface.Train());

Если бы кто-то мог сообщить мне, что мне здесь не хватает, это поведение не то, что я ожидал. Когда я обнаружил эту ошибку в своем коде, я смог ее решить, и вот что я ищу - это "Почему" это происходит.

Вот DogTrainerWithNoInterface что, вероятно, основная часть головоломки (Trainer:ITrainer)

public class DogTrainerWithNoInterface : Trainer
{
    public DogTrainerWithNoInterface(IAnimal animal)
        : base(animal)
    { }

    public new string Train()
    {
        return $"Speak Ubu, Speak : {Animal.Speak()}";
    }
}

1 ответ

ПРИМЕЧАНИЕ. Этот ответ предназначен исключительно для описания "новой" проблемы из-за несоответствия в комментарии. Пожалуйста, игнорируйте голосование, так как оно не полностью отвечает на его вопрос.

Образец по ссылке ОП:

public class DogTrainer : Trainer, ITrainer
{
    public DogTrainer(IAnimal animal)
        : base(animal)
    { }

    public new string Train()
    {
        return $"Speak Ubu, Speak : {Animal.Speak()}";
    }
}

Не использовать new при объявлении методов / свойств. Сделать их virtual на базе, а затем override их. То, что вы делаете, это скрытие метода на своей базе.

Вы можете получить только свой new метод для вызова при работе с этим конкретным типом (DogTrainer). Любое приведение к базе вызовет его метод, то есть работу с переменной, объявленной как Trainer или же ITrainer,

Зная, когда использовать переопределение и новые ключевые слова (Руководство по программированию в C#)

Другие вопросы по тегам