Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphic nature of using Func<T> in C# [duplicate]

I ran into a interesting issue last week and I am not sure I truly understand the polymorphic nature of the following code. I created this sample based some code that I wrote.

Basic Setup

  • I have a class "Trainer" that knows how to train a animal.
  • I use that as a base class and created a subclass "DogTrainer" that knows how to only train dogs.
  • I created a function pointer using the superclass as the return type.
  • I then call that function getting a new instance of the subclass "DogTrainer".
  • I then call the "Train" method of the instance returned from the function pointer.
  • The "Train" method invokes the "Trainer" - "Train" method not the "DogTrainer" - "Train" method as expected

Here is the code

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

Now if I use a Interface as the return type it does work as expected "IF" I have the interface directly marked on the "DogTrainer" subclass

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

If I do not have the interface on the subclass it does not work as expected. See Example

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

If someone could let me know what I am missing here, this behavior is not what I expected. When I discovered this bug in my code I was able to solve it, what I am looking for here is the "Why" this is happening.

Here is DogTrainerWithNoInterface which is likely the main part of the puzzle (Trainer:ITrainer)

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

    public new string Train()
    {
        return $"Speak Ubu, Speak : {Animal.Speak()}";
    }
}
like image 922
Craig Selbert Avatar asked Dec 14 '25 19:12

Craig Selbert


1 Answers

NOTE: This answer is purely for describing the "new" issue due to not fitting in the comment. Please ignore voting as it doesn't fully answer his question.

Sample from OP's link:

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

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

Do not use new when declaring methods/properties. Make them virtual on the base and then override them. What you are doing is hiding the method on your base.

You can only get your new method to invoke when working with that specific type (DogTrainer). Any down-casting to the base will invoke it's method i.e. working with a variable declared as Trainer or ITrainer.

Knowing When to Use Override and New Keywords (C# Programming Guide)

like image 174
TyCobb Avatar answered Dec 17 '25 11:12

TyCobb



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!