如何使用反射转换为通用接口?

Sin*_*tic 2 c# generics reflection

我有一个字典,我正在使用它来促进基于api版本号的内部路由.基本上发生的是我在字典中查找和操作并尝试调用它的RUN方法.但为了做到这一点,我需要能够将对象转换为它的接口.这就是我的意思,这是字典:

    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>));
    }
Run Code Online (Sandbox Code Playgroud)

我可以抓住我想要实例化的正确类型:

var myOperation = Routing["2.0"];
Run Code Online (Sandbox Code Playgroud)

然后在正常情况下,我只是实例化并将其转换为:

var myInstance = (MyInterface) Activator.CreateInstance(myOperation);
Run Code Online (Sandbox Code Playgroud)

但是,接口是通用的,因为它需要知道Request和Response类型是什么:

var myInstance = (MyInterface<TRequest, TResponse>) Activator.CreateInstance(myOperation);
Run Code Online (Sandbox Code Playgroud)

我不知道如何告诉它在这个阶段的请求和响应类型是什么.我假设它可以用反射来完成.我发现我可以通过像myOperation.GetGenericArguments()之类的东西来获取那些通用参数,但是我不知道如何在这个阶段使用它对我有利.有谁知道如何做到这一点?

SLa*_*aks 5

这本质上是不可能的。
您不能在编译时表达这种类型,因为直到运行时您才知道类型参数。
因此,您不能强制转换为它,也不能拥有该类型的变量。

如果可能,您应该使接口协变,并将其强制转换为使用基类型。

或者,您可以创建一个非通用或协变的基本接口并使用它。


Pol*_*ity 5

为了扩展SLaks答案:

没有合理的方法来处理您的场景.您要做的是在运行时使用不同类型的单个代码.这只能通过构建2个单独的代码分支(可以完成)或回退到动态/反射来完成.澄清一下:

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);
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以坚持使用SLaks为Apple和Pear宣布基本类型的提议,即水果.这样,Handle可以接受Fruit并对苹果和梨上的常用功能执行逻辑.

这引出了一个问题,如何使用泛型来做到这一点.默认情况下,泛型不支持方差,但在.NET 4.0中它当然是可能的.您可以通过在type参数上应用out关键字将接口(但只是一个接口)声明为协变.这允许您执行以下操作:

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>());
Run Code Online (Sandbox Code Playgroud)