Invoking a method via reflection with generics and overrides

前端 未结 2 1977
孤城傲影
孤城傲影 2021-01-18 08:40

I\'m trying to invoke the RegisterType method in the Unity container. RegisterType has a total of 16 overrides (some of those are parameters some are types).

I\'m tr

相关标签:
2条回答
  • 2021-01-18 09:31

    My initial post mentioned that I had tried passing null as a 3rd parameter and that the app "croaked." Specifically, it was getting a null reference exception and I should have been more clear about that.

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

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

    Thanks to Jason for his help. His sample sent me down the path that eventually led to the answer.

    0 讨论(0)
  • 2021-01-18 09:36

    I can't pass null orMissing.Value either, or it croaks.

    Croaks how? You should be able to pass null for a params parameter (when you invoke a method like M(params object[] objects) via M() it will be the case that objects is null within the method.

    Second, you can lookup the method more cleanly. I don't have a compiler at my fingertips, but try this:

    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 invoke the method like so, passing null for the params InjectionMember[] parameter:

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

    Sorry if it doesn't compile. If it doesn't compile, this will get you very close to a correct solution.

    Here is a self-contained 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 });
            }
        }
    }
    

    This prints:

    Hello, world!
    17
    True
    DateTime
    

    on the console.

    I've tried with and without BindingFlags.OptionalParamBinding. I'm stumped.

    params is not part of the signature of a method. It is a compiler trick to allow variable-length parameter lists. BindingFlags.OptionalParamBinding is for binding optional parameters to their default values.

    0 讨论(0)
提交回复
热议问题