GWL*_*osa 8 .net c# reflection expression-trees
所以我有一些代码在对象上设置属性.此代码来自我们在单元测试中使用的内部验证类.所以代码可能会提供类似的东西
private static void SetDeepValue(object targetObject, Expression<Func<string>> propertyToSet, object valueToSet)
{
var underlyingProperty = ((PropertyInfo)((MemberExpression)propertyToSet.Body).Member);
underlyingProperty.SetValue(targetObject, valueToSet);
}
Run Code Online (Sandbox Code Playgroud)
此代码在单元测试类型环境中使用,然后我们可以在其中进行调用
foreach (string currentTestCaseValue in TestCaseSets)
{
BusinessObject myCustomer = new BusinessObject();
SetDeepValue(myCustomer, ()=>myCustomer.FirstName,currentTestCaseValue);
ValidateBusinessRules(myCustomer);
}
Run Code Online (Sandbox Code Playgroud)
(为简洁/复杂而简化的代码)
但是,现在,由于一些重构,我们留下了类似的东西:
foreach (string currentTestCaseValue in TestCaseSets)
{
BusinessObject myCustomer = new BusinessObject();
SetDeepValue(myCustomer, ()=>myCustomer.NameInfo.First,currentTestCaseValue);
ValidateBusinessRules(myCustomer);
}
Run Code Online (Sandbox Code Playgroud)
当此代码运行时,我们收到错误:
对象与目标类型不匹配.
我怀疑它试图打电话First给BusinessObject,而不是NameInfo.如何修改我的代码来处理这个'嵌套'的情况?
以下是通常将字符串转换"ColumnName1.ColumnName2"为lambda表达式的方法x => x.ColumnName1.ColumnName2:
Expression<Func<T, object>> ForNestedProperty(string columnName)
{
// x
ParameterExpression param = Expression.Parameter(typeof(T), "x");
// x.ColumnName1.ColumnName2
Expression property = columnName.Split('.')
.Aggregate<string, Expression>
(param, (c, m) => Expression.Property(c, m));
// x => x.ColumnName1.ColumnName2
Expression<Func<T, object>> lambda = Expression.Lambda<Func<T, object>>(
Expression.Convert(property, typeof(object)), param);
return lambda;
}
Run Code Online (Sandbox Code Playgroud)
(从这里复制)
既然你给了我们一个例子,那就很容易做到了。以任何方式编译表达式都是没有用的,因为我们无法重用它,所以它只会减慢方法的速度。更容易遍历 getter 的“链”并使用反射来访问它们的值。我写的方法同时支持字段(通常用作readonly字段)和属性。
public static void SetDeepValue<T>(object notUsed, Expression<Func<T>> propertyToSet, T valueToSet)
{
List<MemberInfo> members = new List<MemberInfo>();
Expression exp = propertyToSet.Body;
ConstantExpression ce = null;
// There is a chain of getters in propertyToSet, with at the
// beginning a ConstantExpression. We put the MemberInfo of
// these getters in members and the ConstantExpression in ce
while (exp != null)
{
MemberExpression mi = exp as MemberExpression;
if (mi != null)
{
members.Add(mi.Member);
exp = mi.Expression;
}
else
{
ce = exp as ConstantExpression;
if (ce == null)
{
// We support only a ConstantExpression at the base
// no function call like
// () => myfunc().A.B.C
throw new NotSupportedException();
}
break;
}
}
if (members.Count == 0)
{
// We need at least a getter
throw new NotSupportedException();
}
// Now we must walk the getters (excluding the last).
// From the ConstantValue ce we take the base object
object targetObject = ce.Value;
// We have to walk the getters from last (most inner) to second
// (the first one is the one we have to use as a setter)
for (int i = members.Count - 1; i >= 1; i--)
{
PropertyInfo pi = members[i] as PropertyInfo;
if (pi != null)
{
targetObject = pi.GetValue(targetObject);
}
else
{
FieldInfo fi = (FieldInfo)members[i];
targetObject = fi.GetValue(targetObject);
}
}
// The first one is the getter we treat as a setter
{
PropertyInfo pi = members[0] as PropertyInfo;
if (pi != null)
{
pi.SetValue(targetObject, valueToSet);
}
else
{
FieldInfo fi = (FieldInfo)members[0];
fi.SetValue(targetObject, valueToSet);
}
}
}
Run Code Online (Sandbox Code Playgroud)
你像这样使用它:
A a = new A();
SetDeepValue(a, () => a.B.C.Value, "Foo");
Run Code Online (Sandbox Code Playgroud)
请注意,SetDeepValue 不需要也不使用targetObject,因为它可以在 getter 链中发现它:
SetDeepValue(myCustomer, ()=>myCustomer.FirstName, currentTestCaseValue);
Run Code Online (Sandbox Code Playgroud)
在这里你有()=>myCustomer。
如果您以以下形式致电,则有必要
SetDeepValue(myCustomer, x=>x.FirstName, currentTestCaseValue);
Run Code Online (Sandbox Code Playgroud)
我什至会给你一个使用第二种格式的方法Expression:
public static void SetDeepValue<TObject, T>(TObject target, Expression<Func<TObject, T>> propertyToSet, T valueToSet)
{
List<MemberInfo> members = new List<MemberInfo>();
Expression exp = propertyToSet.Body;
// There is a chain of getters in propertyToSet, with at the
// beginning a ParameterExpression. We put the MemberInfo of
// these getters in members. We don't really need the
// ParameterExpression
while (exp != null)
{
MemberExpression mi = exp as MemberExpression;
if (mi != null)
{
members.Add(mi.Member);
exp = mi.Expression;
}
else
{
ParameterExpression pe = exp as ParameterExpression;
if (pe == null)
{
// We support only a ParameterExpression at the base
throw new NotSupportedException();
}
break;
}
}
if (members.Count == 0)
{
// We need at least a getter
throw new NotSupportedException();
}
// Now we must walk the getters (excluding the last).
object targetObject = target;
// We have to walk the getters from last (most inner) to second
// (the first one is the one we have to use as a setter)
for (int i = members.Count - 1; i >= 1; i--)
{
PropertyInfo pi = members[i] as PropertyInfo;
if (pi != null)
{
targetObject = pi.GetValue(targetObject);
}
else
{
FieldInfo fi = (FieldInfo)members[i];
targetObject = fi.GetValue(targetObject);
}
}
// The first one is the getter we treat as a setter
{
PropertyInfo pi = members[0] as PropertyInfo;
if (pi != null)
{
pi.SetValue(targetObject, valueToSet);
}
else
{
FieldInfo fi = (FieldInfo)members[0];
fi.SetValue(targetObject, valueToSet);
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以比较两者以查看差异。
| 归档时间: |
|
| 查看次数: |
2147 次 |
| 最近记录: |