Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstract method with strongly typed return type

Consider the following classes :

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

public class Monkey : Animal
{
    public override Animal GiveBirth()
    {
        return new Monkey();
    }
}

public class Snake : Animal
{
    public override Animal GiveBirth()
    {
        return new Snake();
    }
}

//That one doesnt makes sense.
public class WeirdHuman: Animal
{
    public override Animal GiveBirth()
    {
        return new Monkey();
    }
}

I'm searching a way to enforce the return types of the overrided GiveBirth method so that it always returns the actual class type, so that no WeirdHuman can give birth to a Monkey.

I feel like the answer is about generic types, but I can't see how I can do that.

Exemple of the expected result :

public abstract class Animal
{
    public abstract /*here a way to specify concrete type*/ GiveBirth();
}

public class Monkey : Animal
{
    public override Monkey GiveBirth() //Must returns an actual Monkey
    {
        return new Monkey();
    }
}

"Absolutely impossible" may be an answer, if clearly explained.

like image 570
Johnny5 Avatar asked Apr 23 '13 14:04

Johnny5


1 Answers

This is co-variant returns and is not supported by C#. I lament this daily. The best you can hope to do to get around it is to use a generic return type and specify a where condition on the generic type, but this can also cause you to run in to other issues down the road with matching generic parameter requirements.

public abstract class Animal<TBirthType> where TBirthType : Animal<TBirthType>
{
    public abstract TBirthType GiveBirth();
}

public class Monkey<TBirthType> : Animal<TBirthType> where TBirthType : Monkey<TBirthType>
{
    public override TBirthType GiveBirth()
    {
        return new Monkey<Monkey>();
    }
}

Alternately, if you don't need any further inheritance, you can close the generic.

public class Monkey : Animal<Monkey>
{
    public override Monkey GiveBirth()
    {
        return new Monkey();
    }
}

Note that covariance alone is still not enough to ensure that no misbehaving derived type can be formed, but it will allow for the type of the return to be specified as the type being used. There still wouldn't be a way to lock it down from the abstract class though. You could perhaps manage a runtime check via reflection from a method implemented at the base level that would check type at runtime, but this could also be very messy.

like image 150
AJ Henderson Avatar answered Sep 22 '22 11:09

AJ Henderson