-1

I have a generic class with a lambda property defined as such:

public class Transformation<TProperty> : TransformationBase
{
    public Func<TProperty, TProperty> Transform { get; private set; }
    ...

I'm trying to compile an Action that can call this Transform property (on a property of Foo). I don't know TProperty at compile-time. I've started with this:

    private static Action<Foo> Compile(Transformation transformation)
    {
        var fooParameter = Expression.Parameter(typeof(Foo));
        var changePropertyValue = Expression.Constant(transformation);
        var transformProperty = Expression.Property(changePropertyValue, "Transform");
        var transfromCall = Expression.Call(transformProperty, ?
    }

How can I call/execute the transformProperty?

EDIT: Foo (which is known a compile time) has an untyped property Value which needs to be transformed using the Transform property of the Transformation:

    public class Foo {
         public object Value { get; set; }
    }

So, hand-written as an example where TProperty is string it would be:

    Foo foo = ... // coming from an external source
    Transformation<string> tranformation = ... // coming from an external source
    foo.Value = transformation.Transform((string)foo.Value);

Except that I don't know the exact type of the Transformation as it is defined in an external assembly. So, instead of string it could be int or something else. That's why I want to use Expression Trees to compile an Action for a given transformation, such that I can call:

    Foo foo = ... // coming from an external source
    TransformationBase transformation = ... // coming from an external source
    Action<Foo> transform = Compile(transformation);
    transform(foo); // should transform foo.Value using the Transform property of 'transformation'

Note: I made Transformation inherit from TransformationBase to clarify this discussion.

Dejan
  • 9,150
  • 8
  • 69
  • 117
  • 2
    I don't really understand the question. What's the relation between `Foo` and `TProperty`? What's the relation between `Transformation` and `Transformation`? Is the `Compile` method on the `Transformation` class? Is using expression trees a requirement or just how you're attempting to do it? – Ben Aaronson Nov 18 '14 at 10:06
  • What are you trying to do will be much easier to understand if you provide very small example of code - for example, what should you `Action` do in fact. – Konrad Kokosa Nov 18 '14 at 10:07
  • @BenAaronson: `Compile` is outside of `Transformation` and `Foo`. I hope my further description clarifies. – Dejan Nov 18 '14 at 11:04

3 Answers3

1

Your problems relate more to the lack of typing around your problem. Foo.Value is loosely typed, but your transform functions are strongly typed. Expression Trees are also strongly typed. Using them doesn't allow you to magically call code in a loosely typed manner.

The solution is either a lot of reflection, or some easy dynamic:

EDIT: I added CompileUntyped which uses ExpressionTrees.I also added CompileReflection, which uses Reflection without ExpressionTrees. I would recommend the one that uses dynamic. It is by far the easiest to read, hence the easiest to maintain.

class Program
{
    static void Main(string[] args)
    {
        var testTransform = new Transformation<string>
            {
                Transform = s => s.ToUpper()
            };
        var a = Compile(testTransform);
        var foo = new Foo
            {
                Value = "test"
            };

        a(foo);

        //foo.Value is now TEST
    }

    public static Action<Foo> CompileReflection(TransformationBase transformation)
    {
        var f = transformation
            .GetType()
            .GetProperty("Transform")
            .GetGetMethod()
            .Invoke(transformation, null) as Delegate;

        return foo => foo.Value = f.DynamicInvoke(foo.Value);

    }

    public static Action<Foo> Compile(TransformationBase transformation)
    {
        return new Action<Foo>(f =>
            {
                dynamic d = f.Value;
                dynamic t = transformation;
                f.Value = t.Transform(d);
            });
    }

    public static Action<Foo> CompileUntyped(TransformationBase transformation)
    {
        var transformType = transformation.GetType();
        var genericType = transformType.GetGenericArguments().First();

        var fooParam = Expression.Parameter(typeof(Foo), "f");

        var valueGetter = typeof(Foo).GetProperty("Value").GetGetMethod();
        var valueSetter = typeof(Foo).GetProperty("Value").GetSetMethod();
        var transformFuncMember = transformType.GetProperty("Transform").GetGetMethod();

        //Equivalent to f => f.Value = transformation.Transform((T)f.Value)
        //Where T is the generic type parameter of the Transformation, and f is of type Foo
        var expression = Expression.Lambda<Action<Foo>>(
            Expression.Call(
                fooParam,
                valueSetter,
                Expression.Invoke(
                    Expression.Property(
                        Expression.Constant(transformation, transformType), 
                        transformFuncMember
                    ),
                    Expression.Convert(
                        Expression.Property(fooParam, valueGetter),
                        genericType
                    )
                )
            ), fooParam
        );
        return expression.Compile();
    }

}

public class TransformationBase { }

public class Transformation<TProperty> : TransformationBase
{
    public Func<TProperty, TProperty> Transform { get; set; }
}

public class Foo
{
    public object Value { get; set; }
}
Shlomo
  • 14,102
  • 3
  • 28
  • 43
  • Yes, this is a possible solution and yes, my problem is a bit odd in respect to typing. But my question is a general one. How can I call a property of type Func using Exrpression Trees. And this, nobody answered so far. – Dejan Nov 19 '14 at 16:18
0

Not sure what are you trying to do BUT if I understand your intentions - I do not see need for compiling Expressions:

private static Action<TProperty> Compile<TProperty>(Transformation<TProperty> transformation)
{
    return new Action<TProperty>(p => transformation.Transform(p));
}
Konrad Kokosa
  • 16,563
  • 2
  • 36
  • 58
  • Sorry, for being a bit too short in my first shot. It should be clear now that the Action must act on `Foo` and not `TProperty`. – Dejan Nov 18 '14 at 11:10
0

See an example, it should give you what you want.

void Main()
{

    var dummyObject = new Dummy { Test = "Hello!" };

    var propertyTransform = Create(dummyObject, "Test");

    propertyTransform(dummyObject);

    Console.WriteLine("Final transformation " + dummyObject.Test);
}

class Dummy {
    public string Test { get; set; }
}

// Define other methods and classes here
public class Transformation<TProperty>
{
    public Func<TProperty, TProperty> Transform { get; set; }
}

public static Action<TObj> Create<TObj>(TObj myObject, string property){
    var prop = myObject
        .GetType()
        .GetProperty(property);

    var val = prop.GetValue(myObject);

    var transformation = Create((dynamic)val);
    var transform = transformation.Transform;

    return obj => {

        var newValue = transform((dynamic)val);

        prop.SetValue(myObject, newValue);
    };
}

public static Transformation<TProperty> Create<TProperty>(TProperty property){

    var transformation = new Transformation<TProperty>();

    // just a dummy hijacking.
    if(typeof(TProperty)==typeof(string)){

        Func<string, string> test = input => "I am changed man!";

        transformation.Transform = (dynamic)test;
    }

    return transformation;
}

Output:

Final transformation I am changed man!

Erti-Chris Eelmaa
  • 25,338
  • 6
  • 61
  • 78
  • Sorry, for being a bit too short in my first shot. I've added more to the description so it should be clear that `Foo` is type that is known a compile time where `TProperty` of the `Transformation` is not. So your suggestion does not help. – Dejan Nov 18 '14 at 11:18
  • @Dejan: `Action = (Foo foo) => transformation.Transform((dynamic)(foo.Value))` should work. – Erti-Chris Eelmaa Nov 18 '14 at 11:39
  • I cannot call `Transform` as it is defined in `Transformation`. I only have a reference to a instance of type `TransformationBase`. – Dejan Nov 18 '14 at 11:48
  • What about `Action = foo => ((dynamic)transformation).Transform((dynamic)(foo.Value))`? – Erti-Chris Eelmaa Nov 18 '14 at 12:11