Reputation: 38013
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
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
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 theExpression
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 ConstantExpression
s 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