jdm5310
jdm5310

Reputation: 79

Conditional method calls in an expression tree

I'm trying to add an additional method call to my expression tree, but I'm slightly confused how to implement it. Here is what I'm currently working with:

    private static Action<object, object> CreateSetter(SetterInfo info)
    {
        var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

        if (propertyInfo == null)
            return (s, v) => { };

        var objParameter = Expression.Parameter(typeof(object));
        var valueParameter = Expression.Parameter(typeof(object));

        //This is the method call I'm trying to add
        if (info.Name[0] == 'G' && info.Type.Name == TaxDataConstant.ParcelFeat)
        {
            var convertParcelFeatCall = Expression.Call(ConvertParcelFeatMethod, valueParameter, Expression.Constant(info.Name));         
        }

        var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, Expression.Constant(propertyInfo.PropertyType));

        var objCast = Expression.Convert(objParameter, info.Type);
        var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);

        var property = Expression.Property(objCast, propertyInfo);

        var assignment = Expression.Assign(property, valueCast);

        var lambda = Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);

        return lambda.Compile();
    }

What I want to happen is:

1) If the name of the type in my SetterInfo object is ParcelFeat and the Properties name begins with 'G' I want to call ConvertParcelFeat on valueParameter and then call ChangeType on the return.

2) If the name of the type is anything other than ParcelFeat call Changetype as normal with out the additional steps

What I'm confused is how to build the conditional. I'm assuming the way I'm doing it in the above code is wrong and I need to use something like Expression.IfThen() to to build the conditional. I'm also unsure how I can chain the method calls like I want.

Upvotes: 0

Views: 172

Answers (1)

Victor
Victor

Reputation: 628

You do not need in Expression.IfThen because for each specific SetterInfo you combine exactly one specific lambda instance.

Just plug in convertParcelFeatCall in proper place of your ExpressionTree and all should work just fine.

So your code might look like:

class Program
{
    static void Main(string[] args)
    {
        var program = new Program();

        var weightLambda = program.DoInternal("Weight").ToString() 
            == "(Param_0, Param_1) => (Convert(Param_0).Weight = Convert(ChangeType(Param_1, System.Object)))";

        var goodiesLambda = program.DoInternal("Goodies").ToString()
            == "(Param_0, Param_1) => (Convert(Param_0).Goodies = Convert(ChangeType(Param_1, ConvertParcelFeat(Param_1, \"Goodies\"))))";

        Console.WriteLine("WeightLambda is Ok: {0}\nGoodiesLambda is Ok: {1}", weightLambda, goodiesLambda);
    }

    public Action<Object, Object> Do(string name)
    {
        return DoInternal(name).Compile();
    }

    public Expression<Action<object, object>> DoInternal(string name)
    {
        var info = new {Name = name, Type = typeof(Program)};
        var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

        var objParameter = Expression.Parameter(typeof(object));
        var valueParameter = Expression.Parameter(typeof(object));

        //This is the method call I'm trying to add
        Expression toBeTypeChanged;
        if (info.Name[0] == 'G' && info.Type.Name == "Program")
        {
            toBeTypeChanged = Expression.Call(ConvertParcelFeatMethod, valueParameter, Expression.Constant(info.Name));
        }
        else
        {
            toBeTypeChanged = Expression.Constant(propertyInfo.PropertyType);
        }

        var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, toBeTypeChanged);

        var objCast = Expression.Convert(objParameter, info.Type);
        var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);

        var property = Expression.Property(objCast, propertyInfo);

        var assignment = Expression.Assign(property, valueCast);

        return Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);
    }

    public object Weight { get; set; }
    public object Goodies { get; set; }

    public static object ChangeType(object valueParameter, object constant)
    {
        return null;
    }

    public static object ConvertParcelFeat(object valueParameter, object constant)
    {
        return null;
    }

    public MethodInfo ConvertParcelFeatMethod
    {
        get { return typeof(Program).GetMethod("ConvertParcelFeat"); }
    }

    public MethodInfo ChangeTypeMethod
    {
        get { return typeof(Program).GetMethod("ChangeType"); }
    }
}

Upvotes: 1

Related Questions