0

When I try define an variable like that:

IVeterinarian<IAnimal> v = (IVeterinarian<IAnimal>)new CatVeterinarian();

These are an example for the interfaces and classes decleration:

interface IAnimal
{
}

class Dog : IAnimal
{
}

class Cat : IAnimal
{
}

interface IVeterinarian<TAnimal> where TAnimal : IAnimal
{
    void Heal(TAnimal animal);
}

class DogVeterinarian : IVeterinarian<Dog>
{
    public void Heal(Dog animal)
    {
    }
}

class CatVeterinarian : IVeterinarian<Cat>
{
    public void Heal(Cat animal)
    {
    }
}

What's the difference btw my example and declaring IEnumerable of strings in a IEnumerable of objects variable?

Why am I getting an InvalidCastException.

Any ideas?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 2
    `IVeterinarian` is not declared anywhere, only the generic version. Of course you get errors :) – Alexander Derck Jan 12 '18 at 19:42
  • Why even cast it when CatVeterinarian is IVeterinarian? There is no need to cast it... – Marko Jan 12 '18 at 19:43
  • `CatVeterinarian` implements `IVeterinarian` not `IVeterinarian`. – Lee Jan 12 '18 at 19:51
  • What's the difference btw my example and declaring IEnumerable of strings in a IEnumerable of objects variable? – Elad Crypto Jan 12 '18 at 19:58
  • 2
    `IEnumerable` is covariant in `T`, while your `IVeterinarian` is not. It also cannot be made covariant since it would allow you call `v.Heal(new Dog())` which would break type safety since `CatVeterinarian.Heal` requires a `Cat` argument. – Lee Jan 12 '18 at 20:04
  • Take a look at [Understanding Covariant and Contravariant interfaces in C#](https://stackoverflow.com/q/2719954/3744182) and [Difference between Covariance & Contra-variance](https://stackoverflow.com/q/2184551/3744182). – dbc Jan 12 '18 at 20:48

2 Answers2

2

You can't create an instance like that;

IVeterinarian v = (IVeterinarian)new CatVeterinarian();

Because, you should pass a type which is implemented from IAnimal;

IVeterinarian<Cat> v = new CatVeterinarian();

EDIT

You are getting cast invalid exception because IVeterinarian<IAnimal> is not IVeterinarian<Cat> even implements it.

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

As you see, IEnumerable uses out parameter and it makes it covariant. Please review this page.

In your case, you can't make your generic as covariant because the generic is being used as signature. By using covariants you can return derived types as you mentioned in the question.

IEnumerable<object> list = new List<string>();

In short, it has different purpose from your case which you want to try.

lucky
  • 12,734
  • 4
  • 24
  • 46
0

In my usecase, I wanted to take care differently each animal. I found an easy way to do it - Here's my solution:

  1. Remove the generic from the IVeterinarian
  2. Create a generic base Veterinarian class which implements IVeterinarian where T implements IAnimal
  3. Create an abstract method in the base class with the same name that gets an animal argument in type T
  4. Implement the original Heal method with calling the abstract method giving the animal argument after casting to T
  5. In the Cat&Dog Veterinarian classes, just override the abstract method

Here's an example:

interface IAnimal
{
}

class Dog : IAnimal
{
}

class Cat : IAnimal
{
}

interface IVeterinarian
{
    void Heal(IAnimal animal);
}

abstract class BaseVeterinarian<T> : IVeterinarian
    where T : IAnimal
{
    public void Heal(IAnimal animal)
    {
        Heal((T)animal);
    }

    protected abstract void Heal(T animal);
}

class DogVeterinarian : BaseVeterinarian<Dog>
{
    protected override void Heal(Dog animal)
    {
    }
}

class CatVeterinarian : BaseVeterinarian<Cat>
{
    protected override void Heal(Cat animal)
    {
    }
}

Be aware that you must send a correct IAnimal object as argument - or it will throw an InvalidCastException.

Now I can make IVeterinarian objects with different implementations almost like I whished in my question.

IVeterinarian v = new CatVeterinarian();
v.Heal(new Cat());

IVeterinarian v2 = new DogVeterinarian();
v2.Heal(new Dog());