Search code examples
c#.netlinqlambdaexpression-trees

Cast function expression tree from object


I've ended up with a situation where an function expression tree Expression<Func<TClass, TProperty>> is assigned to a global variable of type object and then later in the code I need to call a different method with the expression. I can't change the global objects type; it has to be object.

The code won't compile when trying to call the second method with the global object unless I cast the object as Expression<Func<TClass, TProperty>>. The problem is I don't know what TProperty is at the point that the second method is called.

I've created a quick demo app to illustrate the point (c# console app written in VS2010) - the real application looks nothing like this.

using System;
using System.Linq.Expressions;

namespace FuncExpressionTree
{
    public class ViewModel
    {
        public string StringProperty { get; set; }
        public int IntProperty { get; set; }
    }

    public class Helper<T>
        where T : ViewModel
    {
        private object _global;

        public void Execute()
        {
            AssignToGlobal((T vm) => vm.StringProperty);

            ProcessGlobal();

            AssignToGlobal((T vm) => vm.IntProperty);

            ProcessGlobal();
        }

        public void AssignToGlobal<TClass, TProperty>(Expression<Func<TClass, TProperty>> expression)
        {
            _global = expression;
        }

        public void ProcessGlobal()
        {
            // invalid cast exception thrown when IntProperty is assigned to _global
            AssignToGlobal((Expression<Func<T, string>>)_global);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            (new Helper<ViewModel>()).Execute();
        }
    }
}

If we focus on the Execute() method.

  1. First global is assigned expression for the string property.
  2. ProcessGlobal executes and works because I'm casting to Expression<Func<T, string>>.
  3. Next global is assigned expression for int property.
  4. ProcessGlobal again executes but its at this point that an invalid cast exception is thrown. It would work if I changed it to cast Expression<Func<T, int>> instead but then the string property wouldn't work. Also Expression<Func<T, object>> throws an invalid cast exception.

I feel like Im missing something and that it should be possible to do something with the System.Linq.Expressions namespace to dynamically invoke the the second method (eg AssignToGlobal within ProcessGlobal in the above example).

So how can I get this to work in a generic way?


Solution

  •     public void ProcessGlobal()
        {
            var globalType = _global.GetType(); // globalType = Expression<Func<TClass, TProperty>>
            var functionType = globalType.GetGenericArguments()[0]; // functionType = Func<TClass, TProperty>
            var functionGenericArguments = functionType.GetGenericArguments(); // v = [TClass, TProperty]
            var method = this.GetType().GetMethod("AssignToGlobal").MakeGenericMethod(functionGenericArguments); //functionGenericArguments = AssignToGlobal<TClass, TProperty>
            method.Invoke(this, new[] { this._global }); // Call AssignToGlobal<TClass, TProperty>)(this._global);
        }