简体   繁体   中英

How to Build Expression Tree for Invoke Method from Generic Interface

I'm sorry to ask, how can i invoke method using other that method.Invoke, because some article said, method.Invoke has slower performance. Actually i'm using .NET Core 3.1.

Example, i have a structure code something like this.

public class Message { }

    public class MyMessageType : Message { }

    public class MyMessageResult
    {
        public bool Status { get; set; }
        public DateTime Date => DateTime.Now;
    }

public interface IEncapsulatedMessageHandlerV2<T, TResult> where T : class
    {
        Task<TResult> HandleMessageResultAsync(T message);
    }

    public abstract class EncasulatedMessageHandlerV2<T, TResult> : IEncapsulatedMessageHandlerV2<T, TResult> where T : Message
    {
        public abstract Task<TResult> HandleMessageResultExecAsync(T message);

        async Task<TResult> IEncapsulatedMessageHandlerV2<T, TResult>.HandleMessageResultAsync(T message)
        {
            var msg = message as T;
            if (msg != null)
                return await HandleMessageResultExecAsync(msg);
            return default;
        }
    }

    public class HandlerV2 : EncasulatedMessageHandlerV2<MyMessageType, MyMessageResult>
    {
        public override Task<MyMessageResult> HandleMessageResultExecAsync(MyMessageType message)
        {
            Console.WriteLine("Yo Async!");
            return Task.FromResult(new MyMessageResult
            {
                Status = true
            });
        }
    }

And i can successfully call using method.Invoke

static TResponse UsingMethodInvoke<TResponse>()
        {
            // Assume, i was build this using MakeGenericMethod
            var type = typeof(IEncapsulatedMessageHandlerV2<MyMessageType, MyMessageResult>);

            var typeActivator = typeof(HandlerV2);
            var instance = Activator.CreateInstance(typeActivator);

            var method = type.GetMethod("HandleMessageResultAsync");
            var tsk = (Task<TResponse>)method.Invoke(instance, new[] { new MyMessageType() });
            var result = tsk.GetAwaiter().GetResult();
            return result;
        }

And i try to using Dynamic too, unfortunately they can't calling through abstract HandleMessageResultAsync instead only through implemented class HandleMessageResultExecAsync

static TResponse UsingDynamicInvoke<TResponse>()
        {
            // Assume, i was build this using MakeGenericMethod
            var typeActivator = typeof(HandlerV2);
            var instance = Activator.CreateInstance(typeActivator);

            var tsk = (Task<TResponse>)((dynamic)instance).HandleMessageResultExecAsync(new MyMessageType());
            var result = tsk.GetAwaiter().GetResult();
            return result;
        }

And i was follow to with stackoverflow Speeding up Reflection Invoke C#/.NET , and i get stuck

static void ActivatorMyMessageResultAsnc()
        {
            var type = typeof(HandlerV2);
            var instance = Activator.CreateInstance(type);
            var method = type.GetMethod("HandleMessageResultAsync", BindingFlags.Instance | BindingFlags.Public);

            var originalType = type;
            // Loop until we hit the type we want.
            while (!(type.IsGenericType) || type.GetGenericTypeDefinition() != typeof(EncasulatedMessageHandlerV2<,>))
            {
                type = type.BaseType;
                if (type == null)
                    throw new ArgumentOutOfRangeException("type");
            }

            var messageType = type.GetGenericArguments()[0]; // MyMessageType

            // Use expression to create a method we can.
            var instExpr = Expression.Parameter(typeof(object), "instance");
            var paramExpr = Expression.Parameter(typeof(Message), "message");
            // (Handler)instance;
            var instCastExpr = Expression.Convert(instExpr, originalType);
            // (MyMessageType)message
            var castExpr = Expression.Convert(paramExpr, messageType);
            // ((Handler)inst).HandleMessage((MyMessageType)message)
            var invokeExpr = Expression.Call(instCastExpr, method, castExpr); // <--- this give me error

// i'm stuck, i don't know what should i do next

            ////// Assume this is build from MakeGeneric too
            ////var delType = typeof(Func<object, Message, Task<MessageResult>>);

            //var lambda = Expression.Lambda<Func<object, Message, Task<object>>>(invokeExpr, instExpr, paramExpr);
            //var compiled = lambda.Compile();
            //Func<Message, Task<object>> hook = x => compiled(instance, x);

Or, is there any other ways to Invoke method by dynamicaly, which is faster that method.Invoke

Thanks in advance,

PS: Sorry for my Bad English

I had a similar problem I was trying to solve a while back. I dug into the Net Core codebase and found a class which is really helpful for invoking methods at runtime without being strongly typed.

https://github.com/dotnet/extensions/blob/ff87989d893b000aac1bfef0157c92be1f04f714/shared/Microsoft.Extensions.ObjectMethodExecutor.Sources/ObjectMethodExecutor.cs

ObjectMethodExecutor lets you call a method on an object using its name. An example of usage is:

var methodInfo = [myInterface].GetMethod("[method-you-want-call]");
var messageTypeInfo = [myClass].GetTypeInfo();
var executor = ObjectMethodExecutor.Create(methodInfo, messageTypeInfo);

await executor.ExecuteAsync(handler, new[] { [data-you-want-to-pass-to-your-object] });

Its used extensively in the SignalR codebase for matching SignalR messages to the internal methods on a hub. Its heavily optimised and has the added benefit of allowing async methods to be called too

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM