C# 使用 Expression.Call 构造 lambda 不喜欢某些类型作为参数?

发布于 2024-10-14 15:42:55 字数 1398 浏览 1 评论 0原文

由于各种原因,我使用表达式树工具动态构建 C# lambda。例如我可以创建一个 Func在运行时如以下代码片段所示。

   public static bool myMethod( object obj ) {  … }

    // Construct a Func<string,bool>
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (string))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);
    var lambda = Expression.Lambda(typeof(Func<string,bool>), callMyMethod, lambdaParams);
    var del = (Func<string,bool>)lambda.Compile();
    del("foo"); // works

但是,如果我使用相同的代码尝试创建 Func或 Func它会在以下奇怪的异常指示的地方爆炸:

    // Construct a Func<DateTime,bool> or perhaps a struct type fails... why?
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (DateTime))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);  // Blows up here…

System.ArgumentException: Expression of type 'System.DateTime' cannot be used for parameter of type 'System.Object' of method 'Boolean myMethod(System.Object)'

所以,字符串有效,而 List可以工作,但 int32 不起作用,DateTime 也不起作用。到底是怎么回事?我不知道 C# 的深层内部结构是如何工作的,但我猜测这是因为 int 确实被作为原语处理,也许 DateTime (作为一个结构)也是如此......

对此的任何帮助将不胜感激。

谢谢, 拍

For various reasons I'm constructing a C# lambda dynamically using the expression tree facilities. e.g. I can make a Func<string,bool> at runtime as shown in the following snippet.

   public static bool myMethod( object obj ) {  … }

    // Construct a Func<string,bool>
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (string))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);
    var lambda = Expression.Lambda(typeof(Func<string,bool>), callMyMethod, lambdaParams);
    var del = (Func<string,bool>)lambda.Compile();
    del("foo"); // works

However if I use the same code to try to make a Func<int,bool> or a Func<DateTime,bool> it blows up where indicated with the following strange exception:

    // Construct a Func<DateTime,bool> or perhaps a struct type fails... why?
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (DateTime))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);  // Blows up here…

System.ArgumentException: Expression of type 'System.DateTime' cannot be used for parameter of type 'System.Object' of method 'Boolean myMethod(System.Object)'

So, string works and List<string> works but int32 does not work nor does DateTime. What is going on? I don't know how the deep internals of C# work but I am guessing it's due to int really being handled as a primitive and maybe DateTime (being a struct) as well...

Any help with this would be greatly appreciated.

thanks,
Pat

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱你是孤单的心事 2024-10-21 15:42:55

据我了解,Expression.Call 不执行值类型参数的自动装箱。我找不到任何相关文档,但在 此论坛页面

一种解决方法是使用 Expression.TypeAs

创建一个 UnaryExpression
表示明确的引用或
装箱转换,其中 null 是
如果转换失败则提供。

在您的情况下,这应该可行:(

var boxedParams = lambdaParams.Select(p => Expression.TypeAs(p, typeof(object)))
                              .ToArray();

var callMyMethod = Expression.Call(myMethod, boxedParams);

如果只有一个参数,则不需要花哨的 lambda)

根据实际使用情况,您可能必须根据所讨论的类型是否需要检查装箱转换是否必要是值类型。

As I understand it, Expression.Call doesn't perform auto-boxing of value-type arguments. I'm unable to find any documentation to that effect, but it is mentioned on this forum page.

One workaround would be to explicitly do the boxing conversion in the expression with Expression.TypeAs.

Creates a UnaryExpression that
represents an explicit reference or
boxing conversion where null is
supplied if the conversion fails.

In your case, this should work:

var boxedParams = lambdaParams.Select(p => Expression.TypeAs(p, typeof(object)))
                              .ToArray();

var callMyMethod = Expression.Call(myMethod, boxedParams);

(You don't need the fancy lambdas if there's only one parameter)

Depending on the real usage, you may have to check if the boxing conversion is necessary depending on whether the type(s) in question is(are) value-type(s).

梦晓ヶ微光ヅ倾城 2024-10-21 15:42:55

检查一下:您必须将 DateTime 装箱,因为 DateTime 不是引用类型!

// Construct a Func<DateTime,bool>
var myMethod = typeof(Program).GetMethod("myMethod");
var param = Expression.Parameter(typeof(DateTime));
var boxy = Expression.TypeAs(param, typeof(object));

var callMyMethod = Expression.Call(myMethod, boxy);
var lambda = Expression.Lambda(typeof(Func<DateTime, bool>), callMyMethod, new ParameterExpression[] { param });
var del = (Func<DateTime,bool>)lambda.Compile();
del(DateTime.Now); // works

Check this out: you have to box the DateTime, since DateTime isnt' a reference type!

// Construct a Func<DateTime,bool>
var myMethod = typeof(Program).GetMethod("myMethod");
var param = Expression.Parameter(typeof(DateTime));
var boxy = Expression.TypeAs(param, typeof(object));

var callMyMethod = Expression.Call(myMethod, boxy);
var lambda = Expression.Lambda(typeof(Func<DateTime, bool>), callMyMethod, new ParameterExpression[] { param });
var del = (Func<DateTime,bool>)lambda.Compile();
del(DateTime.Now); // works
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文