[英]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.