Dynamically generate expression of property and empty argument
Asked Answered
D

2

3

Note: please pay attention carefully this is not a duplicate.

I need to create the following Lambda expression:

() => model.property

the model and its property will be determine at runtime. I want a function that takes the model and property and generate the expression:

public object GenerateLambda(object model, string property) 
{

}

If it is possible I don't want the function to be generic. but I think the main problem that I have is with () expression.

Update : The return type of GenerateLambda is not important for me right now. Any result that could be replaced instead of ()=>model.property is accepted. The reason that I used object is that I don't know the generic types of properties and they should be dynamic, but as I tested it is possible to cast object to Expression<Func<TValue?>> which is the the final type that I need(TValue is the property type but it will be determined at runtime).

I have created a series of Blazor components that have a property(namely For) of type Expression<Func<TValue?>> which is used to extract custom attribute of models. The way I use this property is by setting it to a Func in this way : () => person.FirstName. Now I need to generate this expression dynamically for each property of the object(model). Suppose that the object and its type themselves are not dynamically created created.

So for each property p in model I want to call GenerateLambda(object model, string property) that should return () => model.p.

pseudo-code:

foreach(propertyInfo p in model){
   var result= GenerateLambda(model, p, X or any parameter that is needed);
   MyComponent.For= result;
    ... // other logics
}
Daberath answered 24/12, 2020 at 5:52 Comment(4)
Please provide more context to the question and clarify the requirements. If you want lambda expression, then object is not the right return type of the desired function public object GenerateLambda(object model, string property), should be public LambdaExpression GenerateLambda(...)? Also provide an example how it will be called and what it will be used for.Chantay
@IvanStoev Thank you I will add more detailsDaberath
Good, but now we have to wait to to get enough reopen votes.Chantay
@IvanStoev it's open now!Daberath
K
0

Something like this:

public static IEnumerable<Func<object>> GetGetters(object obj)
{
    var type = obj.GetType();

    var obj2 = Expression.Constant(obj);

    foreach (var prop in type.GetProperties())
    {
        Expression prop2 = Expression.Property(obj2, prop);

        // The boxing for value type is explicit, 
        // downcasting to reference type is implicit
        if (prop2.Type.IsValueType)
        {
            prop2 = Expression.Convert(prop2, typeof(object));
        }

        var lambda = Expression.Lambda<Func<object>>(prop2);
        var compiled = lambda.Compile();
        yield return compiled;
    }
}

Use like this:

var model = new
{
    Prop1 = 1,
    Prop2 = new[] { 1, 2, 3 },
    Prop3 = "Hello"
};

var test = GetGetters(model).ToArray();

This is a v1 of the code... A better version would create closures around the obj and cache the expression trees... Not sure if it is really possibible. Mmmh no... currying with Expression trees doesn't seem to be possible. Creating a method that returns another method is a big no no with Expression trees. You need Reflection emit.

To be clear, the optimum would be to be able to generate this:

public static Func<object>[] MakeGetterProp1(MyClass obj)
{
    Func<object> fn1 = () => obj.Prop1;
    Func<object> fn2 = () => obj.Prop2;
    return new[] { fn1, fn2 };
}

through the use of an expression tree. This method would be built the first time and cached. Then you could call it and receive a set of Fun<object> "closed" around a particular obj. Not possible I would say.

Kiarakibble answered 24/12, 2020 at 18:56 Comment(1)
Thank you, and it worked without any problem. Although I removed the compile part the expression itself did the job.Daberath
A
0

Writing reflection and expressions dynamically with high performance has already been solved. One example is this great open source library that does it and caches the results in expressions for maximum performance: https://github.com/ekonbenefits/dynamitey

Allie answered 24/12, 2020 at 18:35 Comment(0)
K
0

Something like this:

public static IEnumerable<Func<object>> GetGetters(object obj)
{
    var type = obj.GetType();

    var obj2 = Expression.Constant(obj);

    foreach (var prop in type.GetProperties())
    {
        Expression prop2 = Expression.Property(obj2, prop);

        // The boxing for value type is explicit, 
        // downcasting to reference type is implicit
        if (prop2.Type.IsValueType)
        {
            prop2 = Expression.Convert(prop2, typeof(object));
        }

        var lambda = Expression.Lambda<Func<object>>(prop2);
        var compiled = lambda.Compile();
        yield return compiled;
    }
}

Use like this:

var model = new
{
    Prop1 = 1,
    Prop2 = new[] { 1, 2, 3 },
    Prop3 = "Hello"
};

var test = GetGetters(model).ToArray();

This is a v1 of the code... A better version would create closures around the obj and cache the expression trees... Not sure if it is really possibible. Mmmh no... currying with Expression trees doesn't seem to be possible. Creating a method that returns another method is a big no no with Expression trees. You need Reflection emit.

To be clear, the optimum would be to be able to generate this:

public static Func<object>[] MakeGetterProp1(MyClass obj)
{
    Func<object> fn1 = () => obj.Prop1;
    Func<object> fn2 = () => obj.Prop2;
    return new[] { fn1, fn2 };
}

through the use of an expression tree. This method would be built the first time and cached. Then you could call it and receive a set of Fun<object> "closed" around a particular obj. Not possible I would say.

Kiarakibble answered 24/12, 2020 at 18:56 Comment(1)
Thank you, and it worked without any problem. Although I removed the compile part the expression itself did the job.Daberath

© 2022 - 2024 — McMap. All rights reserved.