8

How should I construct Expression tree for string.IndexOf("substring", StringComparison.OrdinalIgnoreCase)?

I can get it working without the second argument: StringComparison.OrdinalIgnoreCase. These are my attempts so far:

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;

Also tried this:

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;

Please remember that I can get it working if I ignore the OrdinalIgnoreCase parameter.

Thanks

nawfal
  • 70,104
  • 56
  • 326
  • 368
ashokgelal
  • 80,002
  • 26
  • 71
  • 84
  • 2
    As ever, when you have problems you should say what's going wrong with what you've tried - do you get an exception, a compile-time error, just wrong behaviour? If it's an error of some kind, what's the error? – Jon Skeet Aug 17 '11 at 07:34
  • Sorry. I'll keep that in mind next time when asking questions. I was getting an ArgumentException. – ashokgelal Aug 17 '11 at 15:13
  • Should help: [how-do-i-create-an-expression-tree-to-represent-string-containsterm-in](http://stackoverflow.com/questions/278684/how-do-i-create-an-expression-tree-to-represent-string-containsterm-in-c) – nawfal Dec 19 '13 at 18:40

3 Answers3

12

I suspect there are two problems.

The first is the way you're getting the method - you're asking for a method with only a single string parameter, instead of one with two parameters:

var methodCall = typeof (string).GetMethod("IndexOf",
                            new[] { typeof (string), typeof(StringComparison) });

The second is the value you're giving - it should be the actual value of the constant, not a string:

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

EDIT: Here's a complete working sample:

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"));
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Is there a way to do same for `contains` method? – atp9 Jul 05 '17 at 21:20
  • @atp9: Yes. What have you tried, and what went wrong? – Jon Skeet Jul 05 '17 at 21:23
  • I have tried something like this:`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) });` But this one gives me an error – atp9 Jul 05 '17 at 21:24
  • @atp9: Given that comments aren't really good for extended pieces of code, I suggest you ask a new question with a [mcve]. – Jon Skeet Jul 05 '17 at 21:25
3

The simplest way to do it would be to get it via a lambda like this:

//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"));

This is much more readable and maintainable than manually building an expression tree.

I'm constantly surprised by the amount of people who dive straight into manually building expression trees. There's no need to when you can get the compiler to do the work for you.

Doctor Jones
  • 21,196
  • 13
  • 77
  • 99
  • 2
    +1, there's no reason to build the expression tree manually when the compiler can do it for you – Thomas Levesque Aug 17 '11 at 09:17
  • 1
    @Thomas: Yes, but we can't see where `left` and `right` are coming from here... they're presumably of type `Expression`, not `string`. – Jon Skeet Aug 17 '11 at 15:17
  • May be this is the better way but the library that I'm working on (https://github.com/ashokgelal/NetFilterFramework) has been built on by manually building an expression tree. For now I just need to add a string comparison method call but may be I need to refactor/ rewrite everything using lambdas. – ashokgelal Aug 17 '11 at 15:17
0

I didn't check the rest of it, but if only the enum poses problems:

Expression.Constant(StringComparison.OrdinalIgnoreCase)

or

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

And you have more options. Or check my answer here.

Edit: forgot paranthesis.

Community
  • 1
  • 1
Vladimir
  • 3,599
  • 18
  • 18