String.IndexOf方法的表达式树

8

如何构建字符串的Expression Tree,其中包含 string.IndexOf("substring", StringComparison.OrdinalIgnoreCase)

如果没有第二个参数StringComparison.OrdinalIgnoreCase,可以使其成功运行。以下是我目前尝试过的:

var methodCall = typeof (string).GetMethod("IndexOf", new[] {typeof (string)});
Expression[] parms = new Expression[]{right, Expression.Constant("StringComparison.OrdinalIgnoreCase", typeof (Enum))};
var exp =  Expression.Call(left, methodCall, parms);
return exp;

我也尝试了这个:

var methodCall = typeof (string).GetMethod(method, new[] {typeof (string)});
Expression[] parms = new Expression[]{right, Expression.Parameter(typeof(Enum) , "StringComparison.OrdinalIgnoreCase")};
var exp =  Expression.Call(left, methodCall, parms);
return exp;

请记住,如果我忽略OrdinalIgnoreCase参数,我可以让它起作用。
谢谢。

2
像往常一样,当你遇到问题时,应该说出你所尝试的内容出了什么问题——你是得到了异常、编译时错误还是只是错误的行为?如果是某种类型的错误,那么是什么错误呢? - Jon Skeet
抱歉,下次提问时我会记住的。我遇到了一个ArgumentException。 - ashokgelal
3个回答

12
我猜测有两个问题。
第一个问题是你获取方法的方式不对 - 你只用一个字符串参数去请求方法,而不是两个参数:
var methodCall = typeof (string).GetMethod("IndexOf",
                            new[] { typeof (string), typeof(StringComparison) });

第二个要点是你所提供的 - 它应该是常量实际的值,而不是一个字符串:
Expression[] parms = new Expression[] { right, 
    Expression.Constant(StringComparison.OrdinalIgnoreCase) };

编辑:这是一个完整的可工作样例:

using System;
using System.Linq.Expressions;

class Test
{
    static void Main()
    {
        var method = typeof (string).GetMethod("IndexOf",
                new[] { typeof (string), typeof(StringComparison) });

        var left = Expression.Parameter(typeof(string), "left");
        var right = Expression.Parameter(typeof(string), "right");

        Expression[] parms = new Expression[] { right, 
                Expression.Constant(StringComparison.OrdinalIgnoreCase) };

        var call = Expression.Call(left, method, parms);
        var lambda = Expression.Lambda<Func<string, string, int>>
            (call, left, right);

        var compiled = lambda.Compile();
        Console.WriteLine(compiled.Invoke("hello THERE", "lo t"));
    }
}

contains方法有没有类似的实现方式? - atp9
@atp9:是的。你尝试了什么,出了什么问题? - Jon Skeet
我尝试过类似这样的代码:MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string),typeof(StringComparison) }); var expressionbody = System.Linq.Expressions.Expression.Call(prop, method, new [] { valuetocompare, System.Linq.Expressions.Expression.Constant(StringComparison.OrdinalIgnoreCase) }); 但是这段代码给了我一个错误。 - atp9
@atp9:鉴于注释不适合用于较长的代码片段,我建议您提出一个新问题,并提供一个 [mcve]。 - Jon Skeet

3
最简单的方法是通过lambda表达式来获取它,代码如下:
//the compiler will convert the lambda into an expression
Expression<Func<string, string, int>> expression = (s1, s2) => s1.IndexOf(s2, StringComparison.OrdinalIgnoreCase);
//compile the expression so we can call it
var func = expression.Compile();
//outputs 2
Console.WriteLine(func("Case Sensitive", "se sensitive"));

使用编译器自动生成表达式树要比手动构建更易读和易于维护。

我经常对那些直接开始手动构建表达式树的人感到惊讶。当你可以让编译器为你完成工作时,就没有必要这样做。


2
+1,当编译器可以为您完成时,没有必要手动构建表达式树。 - Thomas Levesque
1
@Thomas:是的,但我们无法看到这里leftright来自哪里...它们可能是Expression类型,而不是string - Jon Skeet
也许这是更好的方法,但我正在使用的库(https://github.com/ashokgelal/NetFilterFramework)已经通过手动构建表达式树来构建。现在我只需要添加一个字符串比较方法调用,但也许我需要使用lambda重构/重写所有内容。 - ashokgelal

0

我没有检查其余部分,但如果只有枚举存在问题:

Expression.Constant(StringComparison.OrdinalIgnoreCase)

或者

Expression.Constant(Enum.Parse(typeof(StringComparison), "OrdinalIgnoreCase"), typeof(Enum));

你还有更多的选择。或者在这里查看我的答案。

编辑:忘记括号了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接