Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use reflection to cast to a generic interface?

I have a dictionary that I'm using to facilitate some internal routing based on api version numbers. Essentially what happens is that I look up and operation in the dictionary and attempt to call it's RUN method. But in order to do this, I need to be able to cast the object to it's interface. Here's what I mean, this is the dictionary:

    public Dictionary<string, Type> Routing = new Dictionary<string, Type>();

    public VersionRouter()
    {
        Routing.Add("1.0", typeof(OperationV1<RequestObjectV1, ResponseObjectV1>));
        Routing.Add("2.0", typeof(OperationV1<RequestObjectV2, ResponseObjectV1>));
        Routing.Add("3.0", typeof(OperationV1<RequestObjectV2, ResponseObjectV2>));
    }

I can grab the correct type that I want to instantiate like so:

var myOperation = Routing["2.0"];

And then under normal circumstances, I'd just instantiate and cast it like this:

var myInstance = (MyInterface) Activator.CreateInstance(myOperation);

However, the interface is generic because it needs to know what the Request and Response types are:

var myInstance = (MyInterface<TRequest, TResponse>) Activator.CreateInstance(myOperation);

I don't know how to tell it what those request and response types are at this stage. I'm assuming it can be done with reflection. I've found that I can get those generic parameters out through something like myOperation.GetGenericArguments() but I'm not sure how to use that to my advantage at this stage. Does anyone know how to do this?

like image 769
Sinaesthetic Avatar asked Dec 21 '22 12:12

Sinaesthetic


1 Answers

In order to expand on SLaks answer:

There is no logical way of dealing with your scenario. What you're trying to do is have a single piece of code work with different types at run time. This can only be done by building separate code branches (which can be done) or by falling back to dynamics/reflection. To clarify this:

class Apple { }
class Pear { }

void Handle(object what)
{
    // either
    if (what is Apple) {}
    else if (what is Pear) {}

    // or
    dynamic x = what;
    x.LetsHopeThisMethodExists();

    // or
    what.GetType().GetMethod('lorem').Invoke(what, null);
}

Now, we can stick with SLaks proposal of declaring a base type for both Apple and Pear, namely Fruit. That way, Handle can accept a Fruit and perform logic on the common functionality available on both Apples and Pears.

That begs the question, how to do this with generics. Generics by default do not support variance, however in .NET 4.0, it's certainly possible. You can declare an interface (but only an interface) as covariant by applying the out keyword on the type parameter. This allows you to do something like:

interface IFruit { }
interface IBasket<out TFruit> where TFruit : IFruit { }

class Apple : IFruit { }
class Pear : IFruit { }

class FruitBasket<TFruit> : IBasket<TFruit> where TFruit : IFruit { }
void Handle(IBasket<IFruit> what) { }

Handle(new FruitBasket<Apple>());
Handle(new FruitBasket<Pear>());
like image 77
Polity Avatar answered Jan 11 '23 13:01

Polity