-1

I have three classes called Animal, Cat and Dog where Cat and Dog inherit from Animal:

public class Animal
{
    public void Talk()
    {
        Console.WriteLine("Parent");
    }
}

public class Cat : Animal
{
    public new void Talk()
    {
        Console.WriteLine("Child(Cat)");
    }
}

public class Dog : Animal
{
    public new void Talk()
    {
        Console.WriteLine("Child(Dog)");
    }
}

I have another method in another class that calls the Talk() method on an animal:

public static Animal FirstKindOfTalk(Animal animal)
    {
        if (animal.GetType() == typeof(Cat))
        {
            var changed = animal as Cat;

            if (changed is not null)
                changed.Talk();

            return changed;
        }
        else if (animal.GetType() == typeof(Dog))
        {
            var changed = animal as Dog;

            if (changed is not null)
                changed.Talk();

            return changed;
        }
            
        return animal;
    }

FirstKindOfTalk(new Animal()); => Parent gets printed in the console. FirstKindOfTalk(new Cat()); => Child(Cat) gets printed in the console. FirstKindOfTalk(new Dog()); => Child(Dog) gets printed in the console.

I have to explicitly cast the parameter to their own type (Cat or Dog) due to Upcasting, in order to be able to run the specific implementation of those classes from the method Talk(). This is because I'm not using virtual and override in my code, if I used them, then the runtime would run the implementation of the passed in parameter from the method.

The Problem I prefer to write the method as the following:

public static Animal SecondKindOfTalk(Animal animal)
    {
        var changed = animal;
        if (animal.GetType() == typeof(Cat))
        {
            changed = animal as Cat;

            changed.Talk();

            return changed;
        }
        else if (animal.GetType() == typeof(Dog))
        {
            changed = animal as Dog;

            changed.Talk();

            return changed;
        }

        return changed;
    }

FirstKindOfTalk(new Animal()); => Parent gets printed in the console. FirstKindOfTalk(new Cat()); => Parent gets printed in the console. FirstKindOfTalk(new Dog()); => Parent gets printed in the console.

What is causing this behavior?

I know using virtual and override is the better way, but I have to work on an existing project where methods from the base class aren't defined as virtual, and I cannot change them either.

4
  • You can shorten the code to if (animal is Cat cat) { cat.Talk(); } and remove the return inside the if as it's the same object anyway. Commented May 18, 2023 at 10:24
  • 1
    In general, please avoid using the new modifier to hide (not replace, not override) already existing members with "new" members. Instead, either use virtual and override. Or pick different names so no hiding occurs, and no confusion arises. In your case, call the method public void Meow() on Cat, and use public void Bark() on Dog, and keep public void Talk() on the base class. Because this naming reflects the reality: You have three unrelated methods, and you can call them only on the instances where they exist. But this removes most of your confusion! Commented May 18, 2023 at 11:13
  • The explanation is that C# is a statically typed language, and when you write something like .Talk, the binding occurs at compile-time, i.e. before you even start running the program, and based on the static compile-time type of the variable (or other expression) on which you use .Talk. This binding is final. It will not suddenly move to another member .Talk when the program runs and it turns out the run-time type of the object is more derived than the compile-time type. Commented May 18, 2023 at 11:17
  • I think FirstKindOfTalk(new Animal()); and SecondKindOfTalk(new Animal()); won't print anything actually because it's neither typeof(Cat) nor typeof(Dog). Commented Sep 27 at 22:12

2 Answers 2

2

Can someone explain to me, what is causing this behavior?

Yes - the compile-time type of changed in SecondKindOfTalk is still Animal , so you're still calling Animal.Talk() regardless of the execution-time type.

It would be simpler to use pattern matching in a switch statement:

public static Animal Task(Animal animal)
{
    switch (animal)
    {
        case Cat cat:
            cat.Talk();
            break;
        case Dog dog:
            dog.Talk();
            break;
    }
    return animal;
}

Or potentially even a switch expression and a delegate (which is slightly less performant, admittedly):

public static Animal Talk(Animal animal)
{
    Action action = animal switch
    {
        Cat cat => cat.Talk,
        Dog dog => dog.Talk,
        _ => null
    };
    action?.Invoke();
    return animal;
}
Sign up to request clarification or add additional context in comments.

Comments

0

What is causing this behavior?

Usage of var which is just an implicitly-typed local variables declaration, i.e.

public static Animal SecondKindOfTalk(Animal animal)
{
    var changed = animal;

is the same as (var is handled by compiler during compile time)

public static Animal SecondKindOfTalk(Animal animal)
{
    Animal changed = animal;

And the following handling does not change the variable type, so the Animal.Talk will be used.

Check out the type-testing operators and cast expressions doc for some patterns which can be used, like pattern matching:

public static Animal SecondKindOfTalk(Animal animal)
{
    if (animal is Cat cat)
    {
        cat.Talk();
    }
    else if (animal is Dog dog)
    {
        dog.Talk();
    }

    return animal;
}

P.S.

Note that changed variable name and attempt to return it kind of imposes some misunderstanding here. No actual change is done to the passed animal, it remains the same instance, i.e. object.ReferenceEquals(changed, animal) is true.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.