'Program+StringEventArgs' 类型的 ParameterExpression 不能用于 'System.EventArgs' 类型的委托参数

ParameterExpression of type 'Program+StringEventArgs' cannot be used for delegate parameter of type 'System.EventArgs'

我正在尝试使用反射将 MethodInfo 转换为 Func<T, TResult>。到目前为止,我的代码在某些情况下似乎可以正常工作,但我无法使用派生自另一个参数类型的参数类型创建 Func。我遗漏了一些东西,不幸的是我不知道如何从那里开始。我想某种 Expression.Convert() 是合适的,如果是的话,在哪里以及如何?

这是一个测试我想要实现的目标的简短程序。它可以复制粘贴到 https://dotnetfiddle.net/ 并且应该正在编译。

using System;
using System.Linq.Expressions;

public class Program
{
    public static void Main()
    {
        var myObject = new MyClass();
        var methodInfo = myObject.GetType().GetMethod("HelloThere");
        var parameterType = methodInfo.GetParameters()[0].ParameterType;
        var parameterExpression = Expression.Parameter(parameterType);
        var callExpression = Expression.Call(Expression.Constant(myObject), methodInfo, parameterExpression);
        var compiledFunction = Expression.Lambda<Func<EventArgs, string>>(callExpression, parameterExpression).Compile();
    }

    public class MyClass
    {
        public string HelloThere(StringEventArgs args)
        {
            return "General Kenobi";
        }
    }

    public class StringEventArgs : EventArgs
    {
        public StringEventArgs(string aString)
        {
            Value = aString;
        }

        public string Value { get; set; }
    }
}

但是,在运行时抛出以下错误:

Run-time exception (line 13): ParameterExpression of type 'Program+StringEventArgs' cannot be used for delegate parameter of type 'System.EventArgs'

当您使用

调用 Lambda 方法时
var compiledFunction = Expression.Lambda<Func<EventArgs, string>>(callExpression, parameterExpression).Compile();

您正在指定委托的类型是 Func。但是,只有当您的参数类型是 EventArgs 并且来自 HelloThere 的 return 类型是字符串时才会出现这种情况。从您收到的错误消息来看,这些不是实际类型;例如,错误指的是嵌套在程序类型中的类型 StringEventArgs。

此外,如果您知道参数类型是 EventArgs,那么动态获取 parameterType 值就没有意义了。

您可以尝试使用 Expression.Lambda 的不同重载。省略类型参数,只调用

 var compiledFunction = Expression.Lambda(callExpression, new ParameterExpression[] { parameterExpression }).Compile();

这应该根据参数表达式的类型和调用表达式的类型动态构造 lambda 类型。

警告:我还没有真正尝试过。

你的代码有两个问题。

首先是您声明了类型为 StringEventArgsParameterExpression,然后您试图将其用作 Func<EventArgs, string> 中的参数。那行不通:您需要使用 EventArgs.

类型声明 ParameterExpression

第二个问题是您基本上是在尝试使用表达式构造此 C# 代码:

public string GeneratedMethod(EventArgs e)
{
    return myClass.HelloThere(e);
}

那不会编译,因为你正在获取一个 EventArgs 类型的对象并试图将它传递给一个接受 StringEventArgs.

的方法

在 C# 中,您可以添加一个强制转换,如果您实际上没有传递 StringEventArgs:

,它将在运行时失败
public string GeneratedMethod(EventArgs e)
{
    return myClass.HelloThere((StringEventArgs)e);
}

等价的表达式是Expression.Convert

把它们放在一起,你会得到:

var myObject = new MyClass();
var methodInfo = myObject.GetType().GetMethod("HelloThere");
var parameterExpression = Expression.Parameter(typeof(EventArgs));
var parameterType = methodInfo.GetParameters()[0].ParameterType;
var methodParameter = Expression.Convert(parameterExpression, parameterType);
var callExpression = Expression.Call(Expression.Constant(myObject), methodInfo, methodParameter);
var compiledFunction = Expression.Lambda<Func<EventArgs, string>>(callExpression, parameterExpression).Compile();

如果你传递的不是StringEventArgs,这当然会在运行时失败并显示InvalidCastException,即使compiledFunction的签名说它可以接受任何EventArgs.

的类型