DivideByzero
DivideByzero

Reputation: 474

Why can't I convert concrete type to interface while using generic constraints

I have a code structure like below

interface IAnimal 
{
    string name;
    int Age;
}

class Tiger : IAnimal
{
    public string name;
    public int Age;
}

class Dog : IAnimal
{
    public string name;
    public int Age;
}

extension class is below

public static class AnimalExtension
{
    public static TAnimalType ConvertTo<TAnimalType>(this Animal animal) where TAnimalType : IAnimal
    {
        TAnimalType concreteAnimal;
        if (animal.Name == "tom")
        {
            concreteAnimal = new Tiger(); // compiler error at this line
        }
        return concreteAnimal;

    }
}

Error : Can't implicitly convert Tiger to TAnimal

Why is the compiler not able to convert Tiger(this class implemented IAnimal) to TAnimal. This seems to be a problem while using generic constraints.

While the below code works fine..

IAnimal myTiger = new Tiger();

Upvotes: 2

Views: 85

Answers (3)

Nick Spreitzer
Nick Spreitzer

Reputation: 10608

Why is the compiler not able to convert Tiger(this class implemented IAnimal) to TAnimal. This seems to be a problem while using generic constraints.

I think the other answers have addressed why your code won't work as written. However, here's an example of how to accomplish what I think you're after. With this extension method, the caller explicitly states which implementation of IAnimal they want to convert* to. The TOut parameter is constrained so that it has a parameterless constructor, so I can create a new instance of it and copy over the values from the original animal instance. If anything, this might give you some food for thought. Hope that helps!

* The word "convert" is a little misleading here. We're definitely not converting anything here.

class Program
{
    static void Main(string[] args)
    {
        Dog d = new Dog { Age = 37, Name = "Nick" };
        Tiger t = d.ConvertTo<Dog, Tiger>();
        Debug.Assert(t.Name == d.Name);
        Debug.Assert(t.Age == d.Age);
    }
}

static class AnimalExtension
{
    public static TOut ConvertTo<TIn, TOut>(this TIn animal) 
        where TIn : IAnimal 
        where TOut : IAnimal, new()
    {
        TOut convertedAnimal = new TOut
        {
            Age = animal.Age,
            Name = animal.Name
        };

        return convertedAnimal;
    }
}

interface IAnimal
{
    string Name { get; set; }
    int Age { get; set; }
}

class Tiger : IAnimal
{
    public int Age { get; set; }
    public string Name { get; set; }
}

class Dog : IAnimal
{
    public int Age { get; set; }
    public string Name { get; set; }
}

Upvotes: 1

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 62002

You have:

class Tiger : IAnimal

and elsewhere a constraint:

where TAnimalType : IAnimal

and then you go:

TAnimalType concreteAnimal;
// ...
concreteAnimal = new Tiger(); // compiler error at this line

But all we (and the compiler) know is that both Tiger and TAnimalType implement the same interface. That does not imply any relation what so ever between Tiger and TAnimalType themselves!

For example TAnimalType could legally be a Dog. In that case you would be putting a Tiger into the Dog variable concreteAnimal. That is not allowed.

Upvotes: 4

Kyle
Kyle

Reputation: 6684

Because TAnimalType could be any animal type, even Dog. If someone called ConvertTo<Dog>(), the code you've written would look like this:

public static Dog ConvertTo(this Animal animal)
{
    Dog concreteAnimal;
    if (animal.Name == "tom")
    {
        concreteAnimal = new Tiger(); // compiler error at this line
    }
    return concreteAnimal;
}

The user asked for a Dog, but you're trying to assign a Tiger and return it. You can't do that because a Tiger is not a Dog.

Upvotes: 4

Related Questions