C# Casting implementation of (interface with generic argument that is also an interface) results to InvalidCastException
First in your declaration
IAnimalHandler<IAnimal>
IAnimal it's the name for a generic type parameter, not the reference to the IAnimal interface. It's confusing. Just redefine in the following way:
public interface IAnimalHandler<out T> where T : IAnimal { }
out modifier is required to be able to perform an assignment operation:
IAnimalHandler<Dog> -> IAnimalHandler<IAnimal>
Read the following article if you would like to know more about covariance in c#:
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/
The casting is not working because IAnimalHandler<Dog>
cannot be stored in IAnimalHandler<IAnimal>
. Let's see an example.
IAnimal dog = new Dog();
// Not working
IAnimalHandler<IAnimal> handler = new DogHandler();
public interface IAnimal { }
public class Dog : IAnimal { }
public class Cat : IAnimal { }
public interface IAnimalHandler<T> where T : IAnimal {
public void Handle(T animal);
}
public class DogHandler : IAnimalHandler<Dog>
{
public void Handle(Dog animal)
{
// Handle logic here...
}
}
public class CatHandler : IAnimalHandler<Cat>
{
public void Handle(Cat animal)
{
// Handle logic here...
}
}
As you can see, it's not possible to assign DogHandler
to the variable handler
. A variable of type IAnimalHandler<IAnimal>
means that the concrete underlying class should have a method with this signature public void Handle(IAnimal animal)
. This method would accept any type that implements IAnimal
. The type DogHandler
only accepts Dog in it's Handle method which is not any IAnimal
implementation.
I don't know exactly what you are trying to do with this, but here's an example that could satisfy your needs
using System;
using System.Linq;
using System.Collections.Generic;
var handlerMap = new Dictionary<Type, IAnimalHandler>();
AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => t.GetInterfaces().Any(i => i.Name.Contains(nameof(IAnimalHandler))))
.ToList()
.ForEach(t =>
{
IAnimalHandler instance = (IAnimalHandler)Activator.CreateInstance(t);
handlerMap[instance.HandledType] = instance;
});
handlerMap[typeof(Dog)].Handle(new Dog());
public interface IAnimal { }
public class Dog : IAnimal { }
public class Cat : IAnimal { }
public interface IAnimalHandler
{
Type HandledType { get; }
public void Handle(IAnimal animal);
}
public class DogHandler : IAnimalHandler
{
public Type HandledType => typeof(Dog);
public void Handle(IAnimal animal)
{
if (animal is Dog dog)
{
// Handle dog here...
Console.WriteLine("Dog handled");
}
else
{
throw new InvalidCastException($"Expected to handle a {typeof(Dog)} got {animal.GetType()}");
}
}
}
I hope it helped you!