How to return from ConditionalExpression.IfThen in tree with MethodCallExpression / lambda?

c# expression-trees linq

Question

I am trying to get an expression tree to conditionally evaluate to a string.

Here is my code thus far:

IQueryable<Category> myCategories = DataUtil.Categories.AsQueryable();

ParameterExpression categoryParameterExpression = Expression.Parameter(typeof (Category), "category");
MemberExpression categoryNameMemberExpression = Expression.PropertyOrField(categoryParameterExpression, "CategoryName");
MemberExpression categoryNameLengthExpression = Expression.Property(categoryNameMemberExpression, typeof (string).GetProperty("Length"));
ConstantExpression constantLengthExpression = Expression.Constant(10, typeof (int));
BinaryExpression greaterThanLengthExpression = Expression.GreaterThan(categoryNameLengthExpression, constantLengthExpression);
var getNameInCapsMethod = typeof (Category).GetMethod("GetNameInCaps", BindingFlags.Instance | BindingFlags.Public);
MethodCallExpression getNameInCapsExpression = Expression.Call(categoryParameterExpression, getNameInCapsMethod, categoryNameMemberExpression);

ConditionalExpression ifGreaterThanLengthGetUpperNameExpression = Expression.IfThen(greaterThanLengthExpression, getNameInCapsExpression); 

// I need something between the lambda and the ConditionalExpression to ensure that the void type is not returned?

var ifGreaterThanLengthGetUpperNameLambdaExpression = Expression.Lambda<Func<Category, string>>(ifGreaterThanLengthGetUpperNameExpression, new ParameterExpression[] { categoryParameterExpression }); 
foreach (var category in myCategories)
{
    var upperName = ifGreaterThanLengthUpperLambda(category);
    System.Windows.MessageBox.Show(upperName);
}

Here is the ArgumentException that is occuring at runtime:

An unhandled exception of type 'System.ArgumentException' occurred in System.Core.dll

Additional information: Expression of type 'System.Void' cannot be used for return type 'System.String'

I have figured out that ConditionalExpression is returning the void type for the "IfFalse" condition.

Here is the screenshot from my Expression Tree Visualizer: ConditionalExpression returning void

I just want the string value. I realize that there is an Expression.IfThenElse on the ConditionalExpression type, but I'm not sure what to put in the Else expression. (I don't want to just pass back an empty string, if possible.) Is there a way for me to ensure that the condition is only evaluated when the preceding BinaryExpression evaluates to true? Regardless, how do I solve this problem?

Accepted Answer

It would probably be helpful to express more fully what you're really trying to achieve (essentially, what would the C# equivalent of your desired expression tree look like). At the moment, your expression tree looks roughly like the equivalent of this:

Func<Category, string> f = (category) => { 
    if (category.CategoryName.Length > 10) {
        category.GetNameInCaps(category.CategoryName);
    }
};

However, the C# compiler won't compile this, because you're not returning a string on any of the code paths, so it's no wonder that the expression tree isn't being compiled as you'd like. There are at least two problems:

  1. You almost certainly want to be using an Expression.Condition instead of an Expression.IfThen. This gives you the equivalent of the ... ? ... : ... syntax in C#, which is an expression that can be used as the return value of your lambda. As a statement, an if (...) { } block always has the type void, as you've found (and this would be true even if you added an else).
  2. You need to figure out what to do when your condition doesn't hold. If you really need to return a string, then you must either
    • Pick some default meaningless value to return (e.g. null or ""), or
    • Throw an exception


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why