Search code examples
c#dynamicreflectioncastingpostsharp

How to cast object to method return type


I want to set args.ReturnValue to instance of an object created from TResponse<T> method called Create.

[Serializable]
public sealed class LogError : OnMethodBoundaryAspect
{
    public override void OnException(MethodExecutionArgs args)
    {
        // Logging part..

        MethodInfo methodInfo = (MethodInfo)args.Method;

        // I want to replace line below comment to get TResponse<T> object instead of dynamic if possible
        dynamic returnValue = Activator.CreateInstance(methodInfo.ReturnType);
        args.ReturnValue = returnValue.Create(CodeMessage.InternalError, MessageType.Error, args.Exception);

        args.FlowBehavior = FlowBehavior.Return;
    }
}

Method ReturnType will always be TResponse<T>, but I don't know how to create instance of TResponse<T> based on method return type. TResponse<T> implements method with this signature:

.Create(CodeMessage.InternalError, MessageType.Error, args.Exception);

Create method is static method that returns TResponse<T> object with parameters set.

Since I didn't know how to do what I want I used Activator to create instance of method return type and store it to dynamic type, but it throws RuntimeBinderException when I call Create method.


Solution

  • Since Create(...) is static, you don't need to create an instance by using the Activator class. Just get a MethodInfo from the ReturnType and invoke it with null as first argument:

    public override void OnException(MethodExecutionArgs args)
    {
        // Logging part..
    
        MethodInfo methodInfo = (MethodInfo)args.Method;
    
        MethodInfo create = methodInfo.ReturnType.GetMethod(
                        "Create",
                        new[] { typeof(CodeMessage), typeof(MessageType), typeof(Exception) });
        args.ReturnValue = create.Invoke(null, new object[] { CodeMessage.InternalError, MessageType.Error, args.Exception });
    
        args.FlowBehavior = FlowBehavior.Return;
    }
    

    MethodInfo.Invoke returns an object. Since MethodExecutionArgs.ReturnValue is also only an object, you don't need to cast to the actual TResponse type.

    Anyway, if you need to set some additional properties on the return value, I would introduce a non-generic interface for TResponse<T>. You could then cast the result value to this interface and set the properties.