【问题标题】:ParameterExpression of type 'Program+StringEventArgs' cannot be used for delegate parameter of type 'System.EventArgs'“Program+StringEventArgs”类型的 ParameterExpression 不能用于“System.EventArgs”类型的委托参数
【发布时间】:2019-11-15 03:40:10
【问题描述】:

我正在尝试使用反射将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'

【问题讨论】:

    标签: c# type-conversion delegates expression func


    【解决方案1】:

    您的代码有两个问题。

    首先是您声明了一个类型为StringEventArgsParameterExpression,然后您尝试将其用作Func&lt;EventArgs, string&gt; 中的参数。这行不通:您需要使用 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

    【讨论】:

      【解决方案2】:

      当您使用

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

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

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-09
        • 1970-01-01
        相关资源
        最近更新 更多