C# 组合lambda表达式以检索嵌套值
我试图创建表达式来访问嵌套结构中的字段或属性 我设法为平面对象上的字段和属性创建了getter和setter(作为lambda表达式)。它是这样工作的:C# 组合lambda表达式以检索嵌套值,c#,C#,我试图创建表达式来访问嵌套结构中的字段或属性 我设法为平面对象上的字段和属性创建了getter和setter(作为lambda表达式)。它是这样工作的: Delegate getter = getGetterExpression(objectType,"PropertyOrFieldName").Compile(); Delegate setter = getSetterExpression(objectType,"PropertyorFieldName").Compile(); 我发现pos
Delegate getter = getGetterExpression(objectType,"PropertyOrFieldName").Compile();
Delegate setter = getSetterExpression(objectType,"PropertyorFieldName").Compile();
我发现post(Marc Gravels answer)使用自定义表达式访问者“链接”这些lambda表达式以访问嵌套对象。如果您有如下示例代码所示的深度(动态)嵌套,那么这是正确的方法(通过链接lambda表达式)吗?还是有更有效的方法来实现这一点
// 'regular' C# Code
obj.PropA.FieldB.FieldC.PropD = "Hello World";
// targeted 'expression approach'
Delegate setter = GetPathSetterLambda(obj.GetType(), "PropA.FieldB.FieldC.PropD").Compile();
setter.DynamicInvoke(obj, "Hello World!");
getter和setter的创建方式如下:
private static LambdaExpression getSetterExpression(Type objectType, string fieldOrPropertyName)
{
ParameterExpression parameterExpression = Expression.Parameter(objectType);
MemberExpression memberExpression = Expression.PropertyOrField(parameterExpression, fieldOrPropertyName);
ParameterExpression valueParameterExpression = Expression.Parameter(memberExpression.Type);
BinaryExpression assignExpression = Expression.Assign(memberExpression, valueParameterExpression);
Type setterType = typeof(Action<,>).MakeGenericType(objectType, memberExpression.Type);
return Expression.Lambda(setterType, assignExpression, parameterExpression, valueParameterExpression);
}
private static LambdaExpression getGetterExpression(Type objectType, string fieldOrPropertyName)
{
ParameterExpression parameterExpression = Expression.Parameter(objectType);
MemberExpression memberExpression = Expression.PropertyOrField(parameterExpression, fieldOrPropertyName);
Type getterType = typeof(Func<,>).MakeGenericType(objectType, memberExpression.Type);
return Expression.Lambda(getterType, memberExpression, parameterExpression);
}
obj.SomeB.SomeC.Foo = "bar";
var getter = GetAccessor<A>("SomeB.SomeC.Foo");
var setter = GetMutator<A>("SomeB.SomeC.Foo");
Console.WriteLine(getter(obj)); // "bar"
setter(obj, "baz");
Console.WriteLine(getter(obj)); // "baz"
obj.SomeB.SomeC.Foo = "bar";
var pa = new PropertyAccessor("SomeB.SomeC.Foo");
Console.WriteLine(pa.Get(obj)); // "bar"
pa.Set(obj, "baz");
Console.WriteLine(pa.Get(obj)); // "baz"
private静态LambdaExpression getSetterExpression(类型objectType,字符串fieldOrPropertyName)
{
ParameterExpression ParameterExpression=Expression.Parameter(objectType);
MemberExpression MemberExpression=Expression.PropertyOrField(parameterExpression,fieldOrPropertyName);
ParameterExpression值ParameterExpression=Expression.Parameter(memberExpression.Type);
BinaryExpression assignExpression=Expression.Assign(memberExpression,valueParameterExpression);
类型setterType=typeof(Action).MakeGenericType(objectType,memberExpression.Type);
Lambda(setterType、assignExpression、parameterExpression、valueParameterExpression);
}
私有静态LambdaExpression getGetterExpression(类型objectType,字符串fieldOrPropertyName)
{
ParameterExpression ParameterExpression=Expression.Parameter(objectType);
MemberExpression MemberExpression=Expression.PropertyOrField(parameterExpression,fieldOrPropertyName);
Type getterType=typeof(Func).MakeGenericType(objectType,memberExpression.Type);
Lambda(getterType、memberExpression、parameterExpression);
}
与使用反射相比,我尝试这样做主要是为了提高性能。编译动态lambda表达式,因此获得对象的直接访问器和变异器可能比重复反射来访问属性更有效。虽然表达式实际上并不打算这样使用,但我下面的实现只是在内部使用它们来创建直接委托来读取或设置值 这就是我想到的。我确保不向外部公开任何表达式,只返回简单的委托。由于lambda表达式的工作方式,它们需要我通过泛型参数提供的基类型。如果在
类型
对象中只有运行时的类型,则可以轻松更改该类型,但还必须更改代理类型。我避免了必须指定实际的属性类型。在访问器中,我只返回一个对象,对于mutator,我在表达式中进行类型转换
public static Func<T, object> GetAccessor<T>(string path)
{
ParameterExpression paramObj = Expression.Parameter(typeof(T), "obj");
Expression body = paramObj;
foreach (string property in path.Split('.'))
{
body = Expression.PropertyOrField(body, property);
}
return Expression.Lambda<Func<T, object>>(body, new ParameterExpression[] { paramObj }).Compile();
}
public static Action<T, object> GetMutator<T>(string path)
{
ParameterExpression paramObj = Expression.Parameter(typeof(T), "obj");
ParameterExpression paramValue = Expression.Parameter(typeof(object), "value");
Expression body = paramObj;
foreach(string property in path.Split('.'))
{
body = Expression.PropertyOrField(body, property);
}
body = Expression.Assign(body, Expression.TypeAs(paramValue, body.Type));
return Expression.Lambda<Action<T, object>>(body, new ParameterExpression[] { paramObj, paramValue }).Compile();
}
这样使用:
private static LambdaExpression getSetterExpression(Type objectType, string fieldOrPropertyName)
{
ParameterExpression parameterExpression = Expression.Parameter(objectType);
MemberExpression memberExpression = Expression.PropertyOrField(parameterExpression, fieldOrPropertyName);
ParameterExpression valueParameterExpression = Expression.Parameter(memberExpression.Type);
BinaryExpression assignExpression = Expression.Assign(memberExpression, valueParameterExpression);
Type setterType = typeof(Action<,>).MakeGenericType(objectType, memberExpression.Type);
return Expression.Lambda(setterType, assignExpression, parameterExpression, valueParameterExpression);
}
private static LambdaExpression getGetterExpression(Type objectType, string fieldOrPropertyName)
{
ParameterExpression parameterExpression = Expression.Parameter(objectType);
MemberExpression memberExpression = Expression.PropertyOrField(parameterExpression, fieldOrPropertyName);
Type getterType = typeof(Func<,>).MakeGenericType(objectType, memberExpression.Type);
return Expression.Lambda(getterType, memberExpression, parameterExpression);
}
obj.SomeB.SomeC.Foo = "bar";
var getter = GetAccessor<A>("SomeB.SomeC.Foo");
var setter = GetMutator<A>("SomeB.SomeC.Foo");
Console.WriteLine(getter(obj)); // "bar"
setter(obj, "baz");
Console.WriteLine(getter(obj)); // "baz"
obj.SomeB.SomeC.Foo = "bar";
var pa = new PropertyAccessor("SomeB.SomeC.Foo");
Console.WriteLine(pa.Get(obj)); // "bar"
pa.Set(obj, "baz");
Console.WriteLine(pa.Get(obj)); // "baz"
以下是基于Poke回答的我的解决方案:
public static LambdaExpression GetFieldOrPropertyLambda(PropertyOrFieldAccessType accessType, Type objectType, string fieldOrPropertyExpression)
{
ParameterExpression initialObjectParameterExpression = Expression.Parameter(objectType, objectType.Name);
Expression pathExpression = initialObjectParameterExpression;
foreach (string property in fieldOrPropertyExpression.Split('.'))
pathExpression = Expression.PropertyOrField(pathExpression, property);
LambdaExpression resultExpression;
switch (accessType)
{
case PropertyOrFieldAccessType.Get:
resultExpression = Expression.Lambda(
getGenericGetFunction(objectType, pathExpression.Type), // This makes it work for valueTypes.
pathExpression,
initialObjectParameterExpression);
break;
case PropertyOrFieldAccessType.Set:
ParameterExpression assignParameterExpression = Expression.Parameter(pathExpression.Type);
BinaryExpression assginExpression = Expression.Assign(pathExpression, assignParameterExpression);
resultExpression = Expression.Lambda(
getGenericSetAction(objectType, assginExpression.Type), // This makes it work for valueTypes.
assginExpression,
initialObjectParameterExpression,
assignParameterExpression);
break;
default: throw new NotImplementedException();
}
return resultExpression;
}
private static Type getGenericGetFunction(Type param1, Type param2)
{
return typeof(Func<,>).MakeGenericType(param1, param2);
}
private static Type getGenericSetAction(Type param1, Type param2)
{
return typeof(Action<,>).MakeGenericType(param1, param2);
}
public静态LambdaExpression GetFieldOrPropertyLambda(PropertyOrFieldAccessType accessType,Type objectType,string fieldOrPropertyExpression)
{
ParameterExpression initialObjectParameterExpression=Expression.Parameter(objectType,objectType.Name);
表达式路径表达式=initialObjectParameterExpression;
foreach(fieldOrPropertyExpression.Split('.')中的字符串属性)
pathExpression=Expression.PropertyOrField(pathExpression,property);
LambdaExpression结果显示;
交换机(访问类型)
{
案例属性YorFieldAccessType。获取:
resultExpression=Expression.Lambda(
getGenericGetFunction(objectType,pathExpression.Type),//这使其适用于valueTypes。
pathExpression,
initialObjectParameterExpression);
打破
案例属性YorFieldAccessType.Set:
ParameterExpression assignParameterExpression=Expression.Parameter(pathExpression.Type);
BinaryExpression-AssignExpression=Expression.Assign(路径表达式,assignParameterExpression);
resultExpression=Expression.Lambda(
getGenericSetAction(objectType,assginExpression.Type),//这使其适用于valueTypes。
ASEngineXpression,
initialObjectParameterExpression,
赋值参数表达式);
打破
默认值:抛出新的NotImplementedException();
}
返回结果显示;
}
私有静态类型getGenericGetFunction(类型param1,类型param2)
{
返回typeof(Func).MakeGenericType(param1,param2);
}
私有静态类型getGenericSetAction(类型param1,类型param2)
{
返回typeof(Action).MakeGenericType(param1,param2);
}
为什么要编写这样的东西,使用字符串编译成表达式,只是为了得到一个getter/setter?这毫无意义;你可以使用简单的反射。表达式的全部要点是,您可以将实际的C#语法与IntelliSense和错误检测结合使用,而不是传递具有某些任意含义的“神奇”字符串。我的目标是将表达式编译成一个委托,以实现性能原因。我认为,一旦将其组合成一个委托,性能将超过反射。我错了吗?而且它不是所有的代码都是第一位的,所以我必须处理那些神奇的字符串。@Servy看到这个Daniel Palme做了allready的测试。在阅读这些结果时注意123.456是123456。欧洲人用点来分组。我一时非常困惑@塞维:人们当然会这么想,但想象和现实往往是不同的。作为一个为表达式树编写了大量编译器但没有为反射层编写任何编译器的人,我只知道一半的故事。我不知道他们的绩效目标是什么,也不知道他们使用了什么技巧来实现这些目标。我的目标是获得更好的绩效(我会编辑我的问题)