Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 625
devinbost Avatar asked May 17 '14 01:05

devinbost


1 Answers

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
like image 82
kvb Avatar answered Nov 15 '22 04:11

kvb