Method invocation by reflection using generics and overrides

I am trying to call the RegisterType method in a Unity container. RegisterType has a total of 16 overrides (some of them are parameters, some of which are types).

I am trying to execute the equivalent:

Container.RegisterType<IMyDataProvider, MockData.MockProvider>("MockData", new ContainerControlledLifetimeManager()) 

Using GetMethod () was a complete failure, so I ended up doing this ugly thing:

  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() }); 

And it works fine, right down to Invoke, which complains because I don't have InjectionMember parameters (they are optional and I have nothing to give). Therefore, according to the documentation, I have to use Type.InvokeMember () to call a method with optional parameters.

So, I did this:

  Binder binder = new BootstrapperBinder(); Container.GetType().InvokeMember("RegisterType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.OptionalParamBinding | BindingFlags.InvokeMethod, binder, Container, new object[] { "MockData", new ContainerControlledLifetimeManager() }); 

My BoostrapperBinder class does this:

  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,TTo](System.String, Microsoft.Practices.Unity.LifetimeManager, Microsoft.Practices.Unity.InjectionMember[])").FirstOrDefault(); return mi.MakeGenericMethod(new Type[] { typeof(ICarrierApprovalDataChangeAccessorEndPoint), mockProvider }); } 

Yes, this is ugly, but I just use it for this occasion, so it does the job.

Now the problem is that she still complains about the absence of the third parameter. I can not skip zero or skipped. So, or she wheezes. I tried with BindingFlags.OptionalParamBinding and without it. I'm at a dead end.

(Edited to accommodate the Container.RegisterType example in the code)

+6
source share
2 answers

My first post mentioned that I tried passing null as the third parameter and that the application was "hoarse". In particular, he was getting an exception for the reference link, and I should have been more clear about that.

The solution was to pass "new InjectionMember [0]" instead of null, so Invoke () should look like this:

 registerTypeSpecific.Invoke(Container, new object[] { "MockData", new ContainerControlledLifetimeManager(), new InjectionMember[0] }); 

Thanks to Jason for the help. His sample sent me along a path that ultimately led to an answer.

+1
source

I cannot pass null or Missing.Value , or it screams.

How are Christians? You must pass null for the params parameter (when calling a method, for example M(params object[] objects) through M() , this will be the case when objects is null in the method.

Secondly, you can find the method more cleanly. I don't have a compiler at my fingertips, but try the following:

 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) }); 

Then call the method this way by passing null for the params InjectionMember[] parameter:

 methodInfo.Invoke( Container, new object[] { "MockData", new ContainerControlledLifetimeManager(), null } ); 

Sorry if it does not compile. If it does not compile, it will bring you closer to the right decision.

Here is a self-sufficient example that works:

 namespace ParamsTest { interface Foo { void M<T>(string s, int n, params object[] objects); } class Bar : Foo { public void M<T>(string s, int n, 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 }); } } } 

Fingerprints:

 Hello, world! 17 True DateTime 

on the console.

I tried with BindingFlags.OptionalParamBinding and without it. I'm at a dead end.

params not part of the method signature. This is a compiler to allow variable length parameter lists. BindingFlags.OptionalParamBinding designed to bind optional parameters to their default values.

+1
source

Source: https://habr.com/ru/post/890426/


All Articles