Joep
Joep

Reputation: 19

Generic overridden method with subclass as return type

Let's say that i have a base class Animal.

public abstract class Animal;

This animal class has the abstract method:

public abstract T Copy<T>() where T : Animal

When this method is overridden in the Lion class:

public class Lion : Animal
{
    string roar = "Roar";
}

i wish to return a copy of this lion without its references. So what i think it should be overridden like is this:

public abstract T Copy<T>()
{
    return new Lion(){ 
        roar = this.roar;
    }
}

but this is not allowed, because Lion cannot be converted to the T type(which is an Animal).

This is a understandable error, becouse not every Animal is necessarily a Lion.

But how can i set this situation up where the copy method knows it supposed to return the subclass, Lion in this case, and not have to do all kinds of casting to the correct type? And still have every animal have a method Copy().

Upvotes: 0

Views: 566

Answers (2)

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391326

You can not use generics in this situation, they don't support what you want to do.

However, if you're on C# 9 you can use the new "Covariant return types" support.

Specifically, you can write your code like this:

public abstract class Animal
{
    public abstract Animal Copy();
}

public class Lion : Animal
{
    public override Lion Copy() => new Lion();
    //               ^
    //  notice that I used Lion here, not Animal
}

If you're calling Copy through a reference declared as being of type Animal but referencing an instance of Lion, you will get back a value typed to Animal, holding a Lion reference.

However, if you're calling Copy through a reference typed to Lion, you will get back a reference typed to Lion as well.

Example:

Animal a = new Lion();
Animal c = a.Copy(); <-- holding a Lion, but type is Animal

Lion l = new Lion();
Lion l2 = l.Copy();  <-- type is now Lion as well

If you're not on C# 9, a different option would be to make the Copy method know the type of the derived class, by making Animal generic:

void Main()
{
    Animal<Lion> a = new Lion();
    Lion c1 = a.Copy(); // <-- notice that this is now typed to Lion as well
    
    Lion l = new Lion();
    Lion c2 = l.Copy();
}

public abstract class Animal<T> where T : Animal<T>
{
    public abstract T Copy();
}

public class Lion : Animal<Lion>
{
    public override Lion Copy() => new Lion();
}

This, however, means you always have to declare Animal as the right type of animal so you lose some of the benefits of having this base class.

Upvotes: 1

MindSwipe
MindSwipe

Reputation: 7855

You don't need to use a generic method in this case, because every Lion is an Animal, so you can simply make Copy return an Animal

public abstract class Animal
{
    public abstract Animal Copy();
}

public class Lion : Animal
{
    public string Roar {get; set;} = "Roar";
    
    public override Animal Copy()
    {
        return new Lion
        {
            Roar = this.Roar
        };
    }
}

Then testing it like so:

public static void Main()
{
    var lion1 = new Lion();
    var lion2 = lion1.Copy();
    
    Console.WriteLine(lion1 == lion2);
}

Prints false to the console, as expected because C# classes are reference types and the default == implementation checks for reference equals on classes and our two lions aren't the same object in memory.

Now every subclass of Animal must implement the method Copy returning an Animal. Which animal that is doesn't matter

Upvotes: 1

Related Questions