通过反射使用泛型和重写调用方法

问题描述

| 我正在尝试在Unity容器中调用RegisterType方法。 RegisterType总共有16个覆盖(其中一些是参数,有些是类型)。 我正在尝试执行以下操作:
Container.RegisterType<IMyDataProvider,MockData.MockProvider>(\"MockData\",new ContainerControlledLifetimeManager())
使用GetMethod()完全失败,所以我最终做了这件丑陋的事情:
     MethodInfo registerTypeGeneric = Container.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance).
        Where(p => p.ToString() == \"Microsoft.Practices.Unity.IUnityContainer RegisterType[TFrom,TTo](System.String,Microsoft.Practices.Unity.LifetimeManager,Microsoft.Practices.Unity.InjectionMember[])\").FirstOrDefault();
     MethodInfo registerTypeSpecific = registerTypeGeneric.MakeGenericMethod( new Type[] { typeof(IMyDataProvider),Assembly.LoadFrom(\"MockData.dll\").GetType(\"MockData.MockProvider\") });
     registerTypeSpecific.Invoke(Container,new object[] { \"MockData\",new ContainerControlledLifetimeManager() });
直到收到Invoke投诉为止,因为我没有InjectionMember参数(它们是可选的,我没有提供任何参数),因此效果很好。因此,根据文档,我必须使用Type.InvokeMember()来调用带有可选参数的方法。 所以我这样做:
     Binder binder = new BootstrapperBinder();
     Container.GetType().InvokeMember(\"RegisterType\",BindingFlags.Instance | BindingFlags.Public | BindingFlags.OptionalParamBinding | BindingFlags.InvokeMethod,binder,Container,new ContainerControlledLifetimeManager() });
我的BoostrapperBinder类执行以下操作:
  public override MethodBase BindToMethod(BindingFlags bindingAttr,MethodBase[] match,ref object[] args,ParameterModifier[] modifiers,System.Globalization.CultureInfo culture,string[] names,out object state)
  {
     Type mockProvider = Assembly.LoadFrom(\"MockData.dll\").GetType(\"MockData.MockProvider\");
     state = new object();
     MethodInfo mi = Container.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance).
        Where(p => p.ToString() == \"Microsoft.Practices.Unity.IUnityContainer RegisterType[TFrom,Microsoft.Practices.Unity.InjectionMember[])\").FirstOrDefault();
     return mi.MakeGenericMethod(new Type[] { typeof(ICarrierApprovalDataChangeAccessorEndPoint),mockProvider });
  }
是的,它很丑陋,但我只在这种情况下使用它,所以它可以完成工作。 现在的问题是,它仍在抱怨缺少第三个参数。我不能传递null或Missing.Value,否则它会崩溃。我尝试了有无BindingFlags.OptionalParamBinding。我很沮丧。 (已编辑以将Container.RegisterType示例放入代码中)     

解决方法

           我不能通过
null
Missing.Value
,否则它会发出嘶哑的声音。 吱吱声如何?您应该能够为
params
参数传递
null
(当您通过
M()
调用类似
M(params object[] objects)
的方法时,方法中
objects
为null的情况。 其次,您可以更清晰地查找该方法。我没有指尖的编译器,但是请尝试以下操作:
var registerTypeMethodInfo = 
     typeof(IUnityContainer).GetMethods()
                            .Where(m => m.Name == \"RegisterType\")
                            .Where(m => m.GetParameters()
                                 .Select(p => p.ParameterType)
                                 .SequenceEqual(new[] {
                                      typeof(string),typeof(LifetimeManager),typeof(InjectionMember[])
                                 })
                            )
                            .Where(m => m.GetGenericArguments().Count() == 2)
                            .SingleOrDefault();
Assert.NotNull(registerTypeMethodInfo);
var methodInfo = 
    registerTypeMethodInfo.MakeGenericMethod(new[] {
        typeof(IMyDataProvider),typeof(MockData.MockProvider)
    });
然后像这样调用方法,将
null
传递给
params InjectionMember[]
参数:
methodInfo.Invoke(
    Container,new object[] { 
        \"MockData\",new ContainerControlledLifetimeManager(),null
    }
);
对不起,如果没有编译。如果没有编译,这将使您非常接近正确的解决方案。 这是一个独立的示例,可以正常工作:
namespace ParamsTest {
    interface Foo {
        void M<T>(string s,int n,params object[] objects);
    }
    class Bar : Foo {
        public void M<T>(string s,params object[] objects) {
            Console.WriteLine(s);
            Console.WriteLine(n);
            Console.WriteLine(objects == null);
            Console.WriteLine(typeof(T).Name);
        }
    }
    internal class Program {
        internal static void Main(string[] args) {
            var genericMethodInfo =
                typeof(Foo).GetMethods()
                    .Where(m => m.Name == \"M\")
                    .Where(m => m.GetParameters()
                       .Select(p => p.ParameterType)
                       .SequenceEqual(new[] {
                           typeof(string),typeof(int),typeof(object[])
                       })
                    )
                    .Where(m => m.GetGenericArguments().Count() == 1)
                    .SingleOrDefault();
            var methodInfo =
                genericMethodInfo.MakeGenericMethod(
                    new[] { typeof(DateTime) }
                );
            var bar = new Bar();
            methodInfo.Invoke(bar,new object[] { \"Hello,world!\",17,null });
        }
    }
}
打印:
Hello,world!
17
True
DateTime
在控制台上。   我尝试了有无BindingFlags.OptionalParamBinding。我很沮丧。
params
不是方法签名的一部分。这是允许可变长度参数列表的编译器技巧。
BindingFlags.OptionalParamBinding
用于将可选参数绑定到其默认值。     ,        我的第一篇文章提到我曾尝试将null作为第三个参数传递,并且应用程序“崩溃了”。具体地说,它收到了null引用异常,对此我应该更加清楚。 解决方案是传递\“ new InjectionMember [0] \”而不是null,因此Invoke()应该看起来像这样:
registerTypeSpecific.Invoke(Container,new object[] { \"MockData\",new InjectionMember[0] }); 
感谢Jason的帮助。他的样本将我带到了最终导致答案的道路上。     

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...