【问题标题】:C# MethodInfo to Callable Func - Expression Tree with generic return typeC# MethodInfo 到 Callable Func - 具有通用返回类型的表达式树
【发布时间】:2021-03-11 00:37:01
【问题描述】:

我正在尝试获取 MethodInfo 并将其转换为可调用的 Func。我已经使用方法签名的同步函数成功地做到了这一点:

public AnyObject MyFuncName(AnotherObject message, IMessage messageMetadata) 

AnyObjectAnotherObject 是任何类。在使用反射提取 MethodInfo 之后,我使用以下代码完成了此操作:

public static Func<Handler, object, IMessage, object> CompileHandler(Type handler, MethodInfo method, Type argumentType)
    {
        var instance = Expression.Parameter(typeof(Handler), "instance");
        var messageParam = Expression.Parameter(typeof(object), "message");
        var messageMetaParam = Expression.Parameter(typeof(IMessage), "messageMetadata");
        var methodCall = Expression.Call(
            Expression.Convert(instance, handler),
            method,
            Expression.Convert(messageParam, argumentType),
            Expression.Convert(messageMetaParam, typeof(IMessage)));

        return Expression.Lambda<Func<Handler, object, IMessage, object>>(
            methodCall,
            instance,
            messageParam,
            messageMetaParam).Compile();
    }

以上工作。它将创建Func,我可以调用该函数。我现在正在尝试使我正在反映的函数异步,并且将具有确切的签名,除非它的返回类型为Task&lt;AnyObject&gt;,它被转换为Func&lt;Handler, object, IMessage, Task&lt;object&gt;&gt;

但是,当我将上面的所有引用 Func&lt;Handler, object, IMessage, object&gt; 更改为 Func&lt;Handler, object, IMessage, Task&lt;object&gt;&gt;

当我这样做时,Expression.Lambda 会抛出这个异常:

Initialization method HandlerUnitTests.TestInitialize 
threw exception. System.ArgumentException: System.ArgumentException: Expression of type 
'System.Threading.Tasks.Task`1[Handler.AnyObject]' cannot be used for return type 
'System.Threading.Tasks.Task`1[System.Object]'

但是,当 Func 签名为Func&lt;Handler, object, IMessage, object&gt; 时,将返回类型转换为对象时,它没有这样的反对意见

编辑:这里的问题是我如何实现上述目标?

【问题讨论】:

  • 您可以编辑您的问题标题并总结您要在此处解决的问题吗?请参阅How to Ask 了解更多信息。
  • 您不能将Task&lt;T&gt; 转换为Task&lt;object&gt;,但您可以编写一个辅助异步方法,该方法接受Task&lt;T&gt; 并返回Task&lt;object&gt; 并在您的表达式中调用它。跨度>
  • @JeremyLakeman 你有任何示例代码可以帮助我更好地理解这一点吗?感谢您的回复!

标签: c# reflection


【解决方案1】:

您可以通过自己的方法传递返回值来转换结果;

private static async Task<object> Unwrap<T>(Task<T> task) => await task;
...
Expression.Call(
    new Func<Task<object>, Task<object>>(Unwrap).Method
        .GetGenericMethodDefinition()
        .MakeGenericMethod(typeof(...)),
    methodCall
)

但听起来您确实解决了错误的问题。您的处理程序可能应该是通用的。也许是这样的;

public abstract class Handler{
   public abstract Task<object> InvokeHandler(object message);
}

public abstract class Handler<M>:Handler{
    public abstract object HandleMessage(M message);
    public virtual Task<object> HandleMessageAsync(M message)
        => Task.FromResult(HandleMessage(message));
    public override Task<object> InvokeHandler(object message)
        => HandleMessageAsync((M)message);
}

现在您的主循环可以调用任何Handler.InvokeHandler,而所有类型转换都发生在抽象泛型基类型中。并且无需任何运行时编译。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-18
    • 2014-05-12
    • 1970-01-01
    • 2015-11-29
    • 2019-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多