如何通过Expression.Lambda <func <t,propertyinfo.declaringtype >>实现值获取

时间:2019-03-07 05:35:19

标签: c#

代码:

static Func<T,object> CompileGetValueExpression<T>(PropertyInfo propertyInfo)
{
    var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
    var property = Expression.Property(instance, propertyInfo);
    var convert = Expression.TypeAs(property, typeof(object));
    var lambda =Expression.Lambda<Func<T,object>>(convert, instance);
    return lambda.Compile();
}

例如

void Main()
{
    var data = new Test{prop1 = 1,prop2="test"};
    var type = data.GetType();
    var props = type.GetProperties();
    foreach (var prop in props)
    {
        var function = CompileGetValueExpression<Test>(prop);
        var result = function(data);
        Console.WriteLine(result);      
    }
}

class Test{
    public int prop1 { get; set; }
    public string prop2 { get; set; }
}

此表达式函数与以下方法完全相同

object GetterFunction(Test i) => i.prop1 as object; 
object GetterFunction(Test i) => i.prop2 as object; 

但是我检查IL,系统使用装箱将类转换为对象类,并在用于确认类时取消装箱,当大量使用时会降低效率。

GetterFunction:
IL_0000:  ldarg.1     
IL_0001:  callvirt    UserQuery+Test.get_prop1
IL_0006:  box         System.Int32
IL_000B:  ret 

所以我想解决这个问题,但不能将System.Type放入泛型中。
下面的代码是我期望的逻辑:

static Func<T,propertyInfo.DeclaringType> CompileGetValueExpression<T>(PropertyInfo propertyInfo)
{
    //..
    return Expression.Lambda<Func<T,propertyInfo.DeclaringType>>(convert, instance).Compile();
}

//after compile
int GetterFunction(Test i) => i.prop1; 
string GetterFunction(Test i) => i.prop2; 

1 个答案:

答案 0 :(得分:2)

如果您想从某个方法返回任意大小的任意值类型而又不事先知道它们的类型,那么您可能就不需要装箱它们了。如果编译器不知道值的类型,那么它就不能在堆栈上为其保留空间,不能在没有方法表头的情况下调用其方法,依此类推,因此它需要执行放入堆中的临时引用类型的“盒子”。

如果添加其他通用名称,则可以通过生成Func<Test,int>来避免装箱 参数CompileGetValueExpression(),但这需要您提前静态地知道属性的类型,以便您可以调用CompileGetValueExpression<Test,int>(),而不能像在示例代码中那样遍历任意类型的属性。

// based on the code from your previous question
static Func<T,TProp> CompileGetValueExpression<T, TProp>(PropertyInfo propertyInfo)
{    
    var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
    var property = Expression.Property(instance, propertyInfo);
    var convert = Expression.Convert(property, typeof(TProp));
    return Expression.Lambda<Func<T,TProp>>(convert, instance).Compile();
}

您也可以在不静态知道类型的情况下生成委托,但是仍然需要将Delegate强制转换为特定的Func<Test,int>才能调用它(至少在没有DynamicInvoke的情况下)这将再次导致拳击。

static Delegate CompileGetValueExpression(PropertyInfo propertyInfo)
{     
    var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
    var property = Expression.Property(instance, propertyInfo);
    var delegateType = typeof(Func<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType);
    return Expression.Lambda(delegateType, property, instance).Compile();
}

根据此代码的实际用例,您可以将正在处理返回的属性值的代码合并到动态生成的方法中。举例来说,如果您始终只为每个值调用Console.WriteLine,则可以在生成的lambda中添加一个ToString()调用,并始终生成一个Func<T, string>。这样,int在将其格式化为字符串之前不必将其装箱。如果您始终将它们发送到BinaryWriter,则可以在lambda等内部调用正确的Write(x)重载。