Shaul Behr
Shaul Behr

Reputation: 38013

Linq expression for comparing two enum values

I'm doing some expression building in my app, which is working most of the time. But when I try to do comparative operations on an enum value, I hit trouble. For example:

expr = Expression.GreaterThanOrEqual(memberExpression, constExpression);

This works fine until memberExpression and constExpression are of type MyEnum; this throws a runtime error:

The binary operator GreaterThanOrEqual is not defined for the types 'MyNamespace.MyEnum' and 'MyNamespace.MyEnum'.

I could get around it some other place by converting the enum values to integers, but that seems wrong, somehow. If I can do a comparative operation between MyEnum values in C#, then why doesn't the Expression builder allow it?

Upvotes: 5

Views: 2751

Answers (2)

CularBytes
CularBytes

Reputation: 10321

I eventually did not convert to integers.

private static void Example(string value, Type type, ParameterExpression pe, string propertyName)
{
    Expression whereValue = null;
    if (type.IsEnumOrNullableEnum())
    {
        whereValue = Expression.Constant(Enum.Parse(type, value));
    }
    Expression propExp = Expression.Property(pe, propertyName);
    Expression ruleExpression = Expression.Equal(propExp, whereValue);//results in: item.MyEnum = A
}

Upvotes: 0

Jon
Jon

Reputation: 437404

I could get around it some other place by converting the enum values to integers, but that seems wrong, somehow.

It shouldn't feel wrong. That's what the compiler itself does, after all -- it either substitutes enum values for their corresponding numeric values (when the enum values are known at compile time) or it generates code that performs the necessary casts at runtime. Note that these casts are not necessarily to int but rather to the backing type of the enum in question.

If I can do a comparative operation between MyEnum values in C#, then why doesn't the Expression builder allow it?

The expression tree you are building looks like it matches what happens in C# code, but in reality it doesn't -- precisely because of what the compiler does behind the scenes as mentioned above.

It is true there is no technical reason that Expression.GreaterThanOrEqual and friends cannot examine their arguments and generate the exact same expression tree that corresponds to what the compiler would do. For example, if you pass in two ConstantExpressions with Type equal to MyEnum the method could use reflection to determine the numeric values that correspond to its arguments and behave as if you had passed in constant expressions of that type instead of throwing. It could also handle the general case (non-constant subexpressions) as well.

However, doing so would mean that the WYSIWYG property of expression trees is lost: you would think that you are generating an expression tree X while in reality you would be generating a different expression tree Y.

That might be perfectly desirable -- and you can certainly write methods of your own that do this -- but it is probably not a good idea as the default behavior (in general the design of C# shuns the DWIM mentality), and/or would not justify the cost of development. Keep in mind that there are a great many places where the compiler does the heavy lifting out of sight so accommodating just a subset of these scenarios would be arbitrary, while accommodating all of them would probably be prohibitive. It would be interesting to see if the availability of Roslyn changes this evaluation.

Upvotes: 4

Related Questions