Search code examples
c#system.reflection

Use reflection to invoke method


While working with Azure ServiceFabric, we are using StateManager.GetStateAsync method to get values, The same method can be used to get the value in object type like classes.

I'm trying with below Code

var typeObj = Activator.CreateInstance(type);
var method = typeof(IActorStateManager).GetMethod(nameof(IActorStateManager.GetStateAsync));
            var generic = method.MakeGenericMethod(type);
            dynamic task = generic.Invoke(typeObj, new[] { stateName })
            object result = await task;

With this approach, I'm getting the exception.

Data: {System.Collections.ListDictionaryInternal}
    HResult: -2146232829
    HelpLink: null
    InnerException: null
    Message: "Object does not match target type."
    Source: "mscorlib"
    StackTrace: "at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
                at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)  
                at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)   
                at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)"
    TargetSite: {Void CheckConsistency(System.Object)}

Solution

  • Most likely, a few things have been confused here. If your code is that what you are actually executing, you are over/misusing the type variable.

    Let's decide that type is a concrete ActorStateManager. Then

    var typeObj = Activator.CreateInstance(type); // typeobj is now ActorStateManager
    

    The error is probably here:

    var generic = method.MakeGenericMethod(type); 
    // generic is now StateManager.GetStateAsync<ActorStateManager>
    

    Your intent was not to create StateManager.GetStateAsync<ActorStateManager>, but StateManager.GetStateAsync<bool> or StateManager.GetStateAsync<DateTime>.

    Additionally, I GetStateAsync takes an (optional?) cancellationToken as a second argument, better to be explicit when doing reflection.

    So here is what you want to do I guess:

        public async Task<object> TryLotsOfTypesAndIgnoreErrors(string stateName)
        {
            var typeObj = Activator.CreateInstance(typeof(ActorStateManager));
            foreach (var typeParam in new[] {typeof(bool), typeof(string)})
            {
                try
                {
                    var method = typeof(IActorStateManager).GetMethod(nameof(IActorStateManager.GetStateAsync));
                    var generic = method.MakeGenericMethod(typeParam);
                    var task = (Task) generic.Invoke(typeObj, new object[] { stateName, CancellationToken.None });
                    await task;
                    return task.GetType().GetProperty(nameof(Task<object>.Result))?.GetValue(task);
                }
                catch
                {
                    // TODO: Catch only exception specific to type mismatch
                }
            }
            return null;
        }