AndyMM
AndyMM

Reputation: 910

Create an expression that will evaluate the result of a delegate and return the consequent or the alternate as the case may be

I want to retrieve the value of a property using a getter expression , but within that expression I want to evaluate a predicate and only return the value of the property if predicate evaluates to false, otherwise return a constant.

Something along the lines of (partially using code from here):

Expression<Func<U, bool>> exp = FuncToExpression(predicate);
var instance = Expression.Parameter(propertyInfo.DeclaringType, "instance");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, typeof(object));
var getLamba = Expression.Lambda(convert, instance);

var evaluate = Expression.Condition(exp, getLamba, Expression.Constant(alternate));

var lambda = Expression.Lambda(evaluate, instance);

return (Func<T, object>)lambda.Compile();

Any help here would be appreciated

Edit
More detail as per Jon's comment:

I am getting the following error on the evaluate variable : {"Argument must be boolean"}

This is the FuncToExpression method :

private static Expression<Func<U, bool>> FuncToExpression<U>(Func<U, bool> predicate)
{
    return argument => predicate(argument);
} 

Edit 2
Complete Sample:

public class Test
{
    public static void Main(string[] args)
    {
        TestPredicate test = new TestPredicate();
        test.Number = 11;
        Func<TestPredicate, object> callDelegate;
        PropertyInfo info = typeof(TestPredicate).GetProperties().Where(a => a.Name == "Number").FirstOrDefault();

        Func<int, bool> f = (x => x > 10 ? true : false);

        if (info != null)
        {
            callDelegate = CreateValueGetDelegate<TestPredicate, int, int>(info, f, -1);

            var item = (int) callDelegate(test);
            Console.WriteLine(item); // expecting -1 here
        }

        Console.Read();
    }

    private static Func<T,object> CreateValueGetDelegate<T,U, S>(PropertyInfo propertyInfo, Func<U, bool> predicate, S alternate)
    {
        if (typeof(T) != propertyInfo.DeclaringType)
        {
            throw new ArgumentException();
        }

        Expression<Func<U, bool>> exp = FuncToExpression(predicate);
        var instance = Expression.Parameter(propertyInfo.DeclaringType, "instance");
        var property = Expression.Property(instance, propertyInfo);
        var convert = Expression.TypeAs(property, typeof(object));
        var getLamba = Expression.Lambda(convert, instance);

        var evaluate = Expression.Condition(exp, getLamba, Expression.Constant(alternate));

        var lambda = Expression.Lambda(evaluate, instance);

        return (Func<T, object>)lambda.Compile();
    }

    private static Expression<Func<U, bool>> FuncToExpression<U>(Func<U, bool> predicate)
    {
        return argument => predicate(argument);
    }  

    public class TestPredicate
    {
        public int Number { get; set; }
    }
}

Upvotes: 0

Views: 2291

Answers (2)

Jon Skeet
Jon Skeet

Reputation: 1500815

It would have helped if you'd said what currently going wrong, but I think you just need to get rid of the first Lambda call. I've made a few changes to the variable names too:

Expression<Func<U, bool>> test = FuncToExpression(predicate);
var parameter = Expression.Parameter(propertyInfo.DeclaringType, "instance");
var property = Expression.Property(parameter, propertyInfo);
var trueOption = Expression.TypeAs(property, typeof(object));
var falseOption = Expression.Constant(alternative);

var conditional = Expression.Condition(test, trueOption, falseOption);

var lambda = Expression.Lambda<Func<T, object>>(conditional, parameter);

return lambda.Compile();

If this doesn't work, please let us know in what way - ideally editing a short but complete sample program into your question.

Upvotes: 2

Josh M.
Josh M.

Reputation: 27791

This extension method will allow you to provide a selector (get the property), validator (validate the property) and a default value:

public static P GetValueOrDefault<T, P>(this T item, Func<T, P> selector, Func<P, bool> validator, P defaultValue)
{
    if (item == null)
        return defaultValue;

    P value = selector(item);

    if (validator == null || !validator(value))
        return defaultValue;

    return value;
}

Upvotes: 1

Related Questions