Dynamically generate expression of property and empty argument

谁说我不能喝 提交于 2020-12-26 09:05:26

问题


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
}

回答1:


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.




回答2:


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



来源:https://stackoverflow.com/questions/65434500/dynamically-generate-expression-of-property-and-empty-argument

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!