Search code examples
c#lambdaexpressionboxingunboxing

Removing an unneeded boxing convert from a c# expression


I'm currently trying to convert an

Expression<Func<T,object>>

to an

Expression<Func<T,bool>> 

Currently the watch shows me that my expression holds

Expression<Func<T,object>> myExpression = model=>Convert(model.IsAnAirplane)

I'd like to simplify this to

Expression<Func<T,bool>> myExpression = model=>model.IsAnAirplane

Currently I only succeed at adding a convert, resulting in:

Expression<Func<T,bool>> myExpression = model=>Convert(Convert(model.IsAnAirplane))

But since the underlying type IS a bool, I should be able to scratch the converts entirely, right? I'm familiar with expression visitors etc, but still can't figure out how to remove the convert.

Edit: this accepted answer to this question Generic unboxing of Expression<Func<T, object>> to Expression<Func<T, TResult>> (that could be a possible duplicate) doesn't work for me ... as the expression gets translated by EF, you can see it does Convert(Convert()) instead of just removing the first convert... , this results in "Unable to cast the type 'System.Boolean' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types."


Solution

  • You should be able to strip out any Convert wrappers using something like this:

    Expression<Func<YourModel, object>> boxed = m => m.IsAnAirplane;
    
    var unboxed = (Expression<Func<YourModel, bool>>)StripConvert(boxed);
    
    // ...
    
    public static LambdaExpression StripConvert<T>(Expression<Func<T, object>> source)
    {
        Expression result = source.Body;
        // use a loop in case there are nested Convert expressions for some crazy reason
        while (((result.NodeType == ExpressionType.Convert)
                   || (result.NodeType == ExpressionType.ConvertChecked))
               && (result.Type == typeof(object)))
        {
            result = ((UnaryExpression)result).Operand;
        }
        return Expression.Lambda(result, source.Parameters);
    }
    

    If you prefer, you could alter StripConvert to return Expression<Func<T,U>> instead of a plain LambdaExpression and perform the cast inside the method itself, but in that case you wouldn't be able to take advantage of type-inferencing for the method call.