Search code examples
c#.netgenericsreflectionmef

Casting classes in generic methods in MEF


I have some classes and interface:

interface IAnimal { }
interface ILiveInZoo { }
class Cat : IAnimal, ILiveInZoo { }

Also, I have some methods and generic methods:

class Context
{
    private static CompositionContainer Container = null;

    public ILiveInZoo GetWhoLivesInZoo(string name)
    {
        if (name == "Cat")
            return new Cat();
        return null;
    }

    public void GiveFood<T>(T animal) where T : IAnimal
    {
        var methods = Container.GetExports<Action<T, EventArgs>, AttributeMetadata>();
        //execute methods
    }
}

And here is a use case:

Context context = new Context();
var cat = context.GetWhoLivesInZoo("Cat");
if (cat is IAnimal animal)
{
   context.GiveFood(animal);
}

As you can see in GiveFood metod I'm using MEF. In use case when I cast cat to IAnimal, in GiveFood method typeof(T) will be IAnimal not Cat. First question is: Instance of cat variable is Cat class. Why when I cast it, typeof(T) will be IAnimal? My problem is when I cast cat to IAnimal interface, in GiveFood method, GetExports method returns method related to IAnimal not to Cat class. I found solution to fix that issue, it is using reflection:

Context context = new Context();
var cat = context.GetWhoLivesInZoo("Cat");
if (cat is IAnimal animal)
{
   MethodInfo method = typeof(Context).GetMethod(nameof(Context.GiveFood));
   MethodInfo generic = method.MakeGenericMethod(animal.GetType());
   generic.Invoke(context, new object[] { animal });
}

Now typeof(T) is Cat class and in GiveFood I can get methods related to Cat class. Is there another way (without using reflection) to solve this issue?


Solution

  • A simple and easy to use Solution could be to use dynamic:

    Context context = new Context();
    var cat = context.GetWhoLivesInZoo("Cat");
    if (cat is IAnimal animal)
    {
        context.GiveFood((dynamic)animal);
    }
    

    Do note however that dynamic uses Reflection internally (with caching to make it a little more performant). So if you really want to avoid Reflection, the Visitor Pattern described in the other answer is probably the way to go.