#c#
#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.Введите в общих чертах.
приведенный ниже код — моя ожидаемая логика:
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:
Если вы хотите возвращать произвольные типы значений произвольных размеров из метода, не зная статически типы заранее, вы, вероятно, не собираетесь их упаковывать. Если компилятор не знает, какого типа будет значение, то он не может зарезервировать для него место в стеке, не может вызывать свои методы без заголовка таблицы методов и так далее, Поэтому ему необходимо перейти во временное «поле» в стиле ссылочного типа в куче.
Вы можете избежать боксирования, сгенерировав 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
для каждого значения, вы могли бы добавить ToString()
вызов в сгенерированный лямбда-код и всегда выдавать Func<T, string>
. Тогда int
не нужно было бы вставлять в рамку перед форматированием в виде строки. Если вы всегда отправляете их в a BinaryWriter
, вы вызываете правильную Write(x)
перегрузку внутри lambda и т.д.
Комментарии:
1. toString и выражение. Lambda(делегатный тип, свойство, экземпляр). Скомпилировать() . Это отличный способ сделать это! Спасибо!