簡體   English   中英

如何通過Expression.Lambda實現值getter <Func<T,propertyInfo.DeclaringType> &gt;

[英]how can implement value getter by Expression.Lambda<Func<T,propertyInfo.DeclaringType>>

碼:

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; 

如果你想從方法中返回任意大小的任意值類型,而不是提前靜態地知道類型,那么你可能不會解決它們。 如果編譯器不知道某個類的類型是什么,那么它就不能在棧上為它保留空間,不能在沒有方法表頭的情況下調用它的方法,等等,所以它需要去進入堆上的臨時引用類型樣式“框”。

如果向CompileGetValueExpression()添加另一個泛型參數,則可以通過生成Func<Test,int>來避免裝箱,但這需要您提前靜態地知道屬性的類型,以便可以調用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)重載等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM