如何在使用反射实现泛型接口的类上调用方法



我想对当前运行的程序集中实现某些接口的对象调用一个方法。

我:

  • 接口IMyContract<T>,其中T是一个类。
  • MyContractModelMyContractModel2两种型号
  • 两个服务:MyContractService实现IMyContract<MyContractModel>MyContractService2实现IMyContract<MyContractModel2>
  • 在宿主体内注射services.AddSingleton<IMyContract<MyContractModel>, MyContractService>()services.AddSingleton<IMyContract<MyContractModel2>, MyContractService2>().

我能够从程序集中获取适当的接口,并从依赖注入中加载服务实例,但我不能调用方法。由于编译器不知道类型,我得到了延迟绑定错误。

这是不可行的吗?如果是这样,你能提出其他的建议吗?感谢任何帮助。谢谢。

下面是我的代码:
// Register services
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IMyContract<MyContractModel>, MyContractService>();
serviceCollection.AddSingleton<IMyContract<MyContractModel2>, MyContractService2>();
var serviceProvider = serviceCollection.BuildServiceProvider();
// Load all contract processors (that implement `IMyContract`) and invoke `Process` on them
var iContractPrcessor = typeof(IMyContract<>);
IEnumerable<Type> allTypesInThisAssembly = Assembly.GetExecutingAssembly().GetTypes();
foreach(Type type in allTypesInThisAssembly)
{
if (type.GetInterface(iContractPrcessor.Name.ToString()) != null)
{
// As expected in 1st iteration, produces IMyContract<MyContractModel> 
Type implementedInterface = type.GetInterface(iContractPrcessor.Name.ToString());
// As expected in 1st iteration, produces MyContractService
var contractService = serviceProvider.GetService(implementedInterface);
MethodInfo method = iContractPrcessor.GetMethod("Process");
//This throws an exception: late bound operation exception
var result = (Task) method.Invoke(contractService, null);
await result.ConfigureAwait(false);
}
}
public interface IMyContract<T> where T : class
{
Task Process();
}
public class MyContractModel
{
public string Name { get; set; } = "";
}
public class MyContractService : IMyContract<MyContractModel>
{
public Task Process()
{
Console.WriteLine("MyContractService Process");
return Task.CompletedTask;
}
}
public class MyContractModel2
{
public string Name { get; set; } = "";
}
public class MyContractService2 : IMyContract<MyContractModel2>
{
public Task Process()
{
Console.WriteLine("MyContractService2 Process");
return Task.CompletedTask;
}
}

typeof(IMyContract<>)(iContractPrcessor.)获得MethodInfo

MethodInfo method = iContractPrcessor.GetMethod("Process");
//               ^^^ typeof(IMyContract<>) ^^^

…但是你试图用不同的类型来调用那个方法——实现接口的具体类型,比如typeof(MyContractService),它实现了IMyContract<MyContractModel>

var result = (Task) method.Invoke(contractService, null);
//                            ^^ object of concrete type ^^

所以你试图从一个类型IMyContract<>调用另一个类型MyContractService的方法。

相反,一旦你得到了具体类型,就得到了"过程"。

改变
MethodInfo method = iContractPrcessor.GetMethod("Process");

MethodInfo method = contractService.GetType().GetMethod("Process");

现在你

  • 获取实现接口的类型
  • 从该类型获取方法
  • 在该类型的实例上调用方法

可以更深入。所有这些都假设具体的服务都有一个名为"Process"的方法。它对应于接口方法。这可能不是真的。可能有多个"过程";方法。

如果你确信你在接口上有正确的方法,但你想确定你在类上有相应的方法,该怎么办?

你可以这样做:

// Get the interface type with the generic type specified
var genericInterfaceType = iContractPrcessor.MakeGenericType(t);
// Get the "Process" method on the interface. 
var genericInterfaceMethod = genericInterfaceType.GetMethod("Process");
// Get a mapping of the interface's members to the concrete "target type" members.
InterfaceMapping interfaceMap = contractService.GetType()
.GetInterfaceMap(genericInterfaceType);
// Find the index of the interface's Process method
// in the interface method collection
var interfaceMethodIndex = interfaceMap.InterfaceMethods.ToList()
.IndexOf(genericInterfaceMethod);
// Get the method from the target type method collection with the same index.
// This is the method you want to invoke. 
// It is the method on the service that implements the interface method.
var contractServiceMethod = interfaceMap.TargetMethods[interfaceMethodIndex];

我从来没有听说过InterfaceMap,因为我从来没有尝试过解决这个问题。

尽管我是在回答这个问题,但我不能排除有一个完全不同的、更容易的解决方案。

最新更新