Search code examples
c#.netlinqexpression-trees

Construct AndAlso/OrElse LINQ expression with custom method


I'm trying to pass in a custom MethodInfo to the Expression.AndAlso and OrElse factory methods (which are for the && and || operators, respectively). These operators use short circuiting, which makes this difficult, but normally the & and | operators (along with the true and false operators) are used. However, the MSDN documentation for Expression.AndAlso/OrElse doesn't mention the true or false operators.

For testing, I've declared a method that uses a normal & operator on two ints:

public static int And(int a, int b) {
    return a & b;
}

Note that the return type must be int rather than bool to avoid an exception.
I then construct the expression:

var expr = Expression.AndAlso(
    Expression.Constant(0),
    Expression.Constant(5),
    new Func<int, int, int>(And).Method
);

This results in an exception:

The user-defined operator method 'And' for operator 'AndAlso' must have associated boolean True and False operators.

Strangely, the error is also thrown if I use a custom struct that has the true and false operators. I can avoid it if the struct overloads the & operator and I pass in that overload, but not if I pass in a different method. Other non-short-circuiting operators work with custom methods though.

The problem is that I don't know how to pass methods for the true and false operators in. I first thought I could maybe combine them as delegates, but the different methods have incompatible signatures. Is there any way to pass these methods in?


The bigger picture

I'm building a system for interpreting expressions to support ahead-of-time compilation. It supports using a custom method for the AndAlso/OrElse operators, currently by taking a custom Func<InterpretedExpression, InterpretedExpression, object> (which works as the expressions are interpreted rather than compiled). This could easily be changed if it causes problems (which would be due to it not having accessible true and false methods).


Note: I'm using Visual Studio's C# Interactive window for testing, but ultimately need to support .NET 3.5 (though information for newer versions is still helpful and appreciated).


Solution

  • The problem is that I don't know how to pass methods for the true and false operators in. I first thought I could maybe combine them as delegates, but the different methods have incompatible signatures. Is there any way to pass these methods in?

    The short answer is no, you can't.

    Looking at the reference source implementation (unfortunately, it should really be in the documentation), looks like the passed method has the following constraints:

    (1) It should be a static non generic method with signature

    static T Method(T arg0, T arg1);
    

    where T cannot be an open generic type.

    (2) The declaring type of the method must have operators true and false defined.

    Since operators true and false require the argument type to be the same as the declaring type, this combined with (1) really constraints the usage to class/struct T declaring the static method and true and false operators.

    In other words, a method with bitwise & / | operator semantics without actually overloading those operators.

    So it can be used only for types like this:

    public struct IntWrapper
    {
        public readonly int Value;
        public IntWrapper(int value) { Value = value; }
        public static IntWrapper And(IntWrapper a, IntWrapper b) { return new IntWrapper(a.Value & b.Value); }
        public static bool operator true(IntWrapper x) { return x.Value != 0; }
        public static bool operator false(IntWrapper x) { return x.Value == 0; }
    }
    

    with usage:

    var expr = Expression.AndAlso(
        Expression.Constant(new IntWrapper(0)),
        Expression.Constant(new IntWrapper(5)),
        new Func<IntWrapper, IntWrapper, IntWrapper>(IntWrapper.And).Method
    );
    

    This I guess limits more general usage you are after.