使用 Expression 为对象赋值
Assign value to object using Expression
我有这个使用表达式注入数据的注入器
/// <summary>
/// Inject a data to an instance of T
/// </summary>
/// <typeparam name="T">The type of object as parameter</typeparam>
/// <param name="data">Object instance to where the data is injected</param>
/// <param name="pairData">Set of data info</param>
public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) where T : IAuditable
{
// posible content of T data
// Employee
// : Code
// : Name
// : ID
// possible content of pairData
// Key: a => a.Code
// Value: "12345"
// Key: a => a.Name
// Value: "Vincent"
// after the injection, the data (Employee) will now have value on each properties: Code, Name
foreach (var _o in pairData)
{
var _value = Expression.Constant(_o.Value);
var _assign = Expression.Assign(_o.Key, _value);
//_lambda.Compile()(data);
//var _lambda = Expression.Lambda<Action<object, T>>(_assign, _value, _o.Key);
//_lambda.Compile()(_o.Value, data);
}
}
我只传了一个表达式和值的集合。
Expression on which where to assign the value (eg. a => a.Code) and
value (eg. "123456")
我想将它分配给一个对象 T,但在我当前的尝试中,我得到了一个 System.ArgumentException:为 lambda 声明提供的参数数量不正确。我被困住了,不知道下一步该怎么做。 :) 任何人都可以为我指出正确的方向来完成这项工作吗?
这是示例用法
Employee _employee = new Employee();
Dictionary<Expression<Func<Employee, object>>, object> _x = new Dictionary
<Expression<Func<Employee, object>>, object>
{
{a => a.Code, "12345"}
};
_employee.InjectData(_x);
Assert.AreEqual("12345", _employee.Code);
我也试过
foreach (var _o in pairData)
{
var _mem = _o.Key.Body as MemberExpression;
var _prop = _mem.Member as PropertyInfo;
var _setMethod = _prop.GetSetMethod();
_prop.SetValue(data, _o.Value);
我可以在这里看到进展,因为私有 属性 已经分配,但 getter 为 NULL
提前致谢
如有任何帮助,我们将不胜感激。
您可以使用以下代码:
public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData)
{
foreach (var pair in pairData)
{
data.SetPropertyValue(pair.Key, pair.Value);
}
}
public static T SetPropertyValue<T>(this T target, Expression<Func<T, object>> memberLamda, object value)
{
var memberSelectorExpression = memberLamda.Body as MemberExpression;
if (memberSelectorExpression == null)
{
return target;
}
var property = memberSelectorExpression.Member as PropertyInfo;
if (property == null)
{
return target;
}
property.SetValue(target, value, null);
return target;
}
基本上,这添加了另一种扩展方法来根据表达式设置 属性。
如果您只想在值实际不同时进行更改(这在使用 INotifyPropertyChanged
时很方便),您可以使用以下代码,它需要一个额外的比较器来检查值是否更改:
public static T SetPropertyValue<T, R>(this T target, Expression<Func<T, R>> memberLamda, R value)
{
return target.SetPropertyValue(memberLamda, value, EqualityComparer<R>.Default);
}
public static T SetPropertyValue<T, R>(this T target, Expression<Func<T, R>> memberLamda, R value, IEqualityComparer<R> comparer)
{
var memberSelectorExpression = memberLamda.Body as MemberExpression;
if (memberSelectorExpression == null)
{
return target;
}
var property = memberSelectorExpression.Member as PropertyInfo;
if (property == null)
{
return target;
}
var currentValue = (R) property.GetValue(target, null);
if (comparer.Equals(currentValue, value))
{
return target;
}
property.SetValue(target, value, null);
return target;
}
注意,这里我们使用比较器来检查当前值是否等于新值。如果是这样,我们只是 return(不需要更新),如果不是我们设置值。
对我来说意义不大,但它是:
public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) where T : IAuditable
{
foreach (var item in pairData)
{
var member = item.Key;
// If member type is a reference type, then member.Body is the property accessor.
// For value types it is Convert(property accessor)
var memberBody = member.Body as MemberExpression ?? (MemberExpression)((UnaryExpression)member.Body).Operand;
var lambda = Expression.Lambda<Action<T>>(Expression.Assign(memberBody, Expression.Constant(item.Value)), member.Parameters);
var action = lambda.Compile();
action(data);
}
}
UPDATE 根据评论中的要求,支持嵌套 class 属性 初始化的版本:
public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) //where T : IAuditable
{
var assignments = new List<Expression>();
foreach (var item in pairData)
{
var member = item.Key;
// If member type is a reference type, then member.Body is the property accessor.
// For value types it is Convert(property accessor)
var memberBody = member.Body as MemberExpression ?? (MemberExpression)((UnaryExpression)member.Body).Operand;
assignments.Clear();
assignments.Add(Expression.Assign(memberBody, Expression.Constant(item.Value)));
var target = member.Parameters[0];
while (memberBody.Expression != target)
{
var childMember = (MemberExpression)memberBody.Expression;
assignments.Add(Expression.IfThen(Expression.ReferenceEqual(childMember, Expression.Constant(null)),
Expression.Assign(childMember, Expression.New(childMember.Type))));
memberBody = childMember;
}
assignments.Reverse();
var body = assignments.Count > 1 ? Expression.Block(assignments) : assignments[0];
var lambda = Expression.Lambda<Action<T>>(body, target);
var action = lambda.Compile();
action(data);
}
}
我有这个使用表达式注入数据的注入器
/// <summary>
/// Inject a data to an instance of T
/// </summary>
/// <typeparam name="T">The type of object as parameter</typeparam>
/// <param name="data">Object instance to where the data is injected</param>
/// <param name="pairData">Set of data info</param>
public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) where T : IAuditable
{
// posible content of T data
// Employee
// : Code
// : Name
// : ID
// possible content of pairData
// Key: a => a.Code
// Value: "12345"
// Key: a => a.Name
// Value: "Vincent"
// after the injection, the data (Employee) will now have value on each properties: Code, Name
foreach (var _o in pairData)
{
var _value = Expression.Constant(_o.Value);
var _assign = Expression.Assign(_o.Key, _value);
//_lambda.Compile()(data);
//var _lambda = Expression.Lambda<Action<object, T>>(_assign, _value, _o.Key);
//_lambda.Compile()(_o.Value, data);
}
}
我只传了一个表达式和值的集合。
Expression on which where to assign the value (eg. a => a.Code) and value (eg. "123456")
我想将它分配给一个对象 T,但在我当前的尝试中,我得到了一个 System.ArgumentException:为 lambda 声明提供的参数数量不正确。我被困住了,不知道下一步该怎么做。 :) 任何人都可以为我指出正确的方向来完成这项工作吗?
这是示例用法
Employee _employee = new Employee();
Dictionary<Expression<Func<Employee, object>>, object> _x = new Dictionary
<Expression<Func<Employee, object>>, object>
{
{a => a.Code, "12345"}
};
_employee.InjectData(_x);
Assert.AreEqual("12345", _employee.Code);
我也试过
foreach (var _o in pairData)
{
var _mem = _o.Key.Body as MemberExpression;
var _prop = _mem.Member as PropertyInfo;
var _setMethod = _prop.GetSetMethod();
_prop.SetValue(data, _o.Value);
我可以在这里看到进展,因为私有 属性 已经分配,但 getter 为 NULL
提前致谢
如有任何帮助,我们将不胜感激。
您可以使用以下代码:
public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData)
{
foreach (var pair in pairData)
{
data.SetPropertyValue(pair.Key, pair.Value);
}
}
public static T SetPropertyValue<T>(this T target, Expression<Func<T, object>> memberLamda, object value)
{
var memberSelectorExpression = memberLamda.Body as MemberExpression;
if (memberSelectorExpression == null)
{
return target;
}
var property = memberSelectorExpression.Member as PropertyInfo;
if (property == null)
{
return target;
}
property.SetValue(target, value, null);
return target;
}
基本上,这添加了另一种扩展方法来根据表达式设置 属性。
如果您只想在值实际不同时进行更改(这在使用 INotifyPropertyChanged
时很方便),您可以使用以下代码,它需要一个额外的比较器来检查值是否更改:
public static T SetPropertyValue<T, R>(this T target, Expression<Func<T, R>> memberLamda, R value)
{
return target.SetPropertyValue(memberLamda, value, EqualityComparer<R>.Default);
}
public static T SetPropertyValue<T, R>(this T target, Expression<Func<T, R>> memberLamda, R value, IEqualityComparer<R> comparer)
{
var memberSelectorExpression = memberLamda.Body as MemberExpression;
if (memberSelectorExpression == null)
{
return target;
}
var property = memberSelectorExpression.Member as PropertyInfo;
if (property == null)
{
return target;
}
var currentValue = (R) property.GetValue(target, null);
if (comparer.Equals(currentValue, value))
{
return target;
}
property.SetValue(target, value, null);
return target;
}
注意,这里我们使用比较器来检查当前值是否等于新值。如果是这样,我们只是 return(不需要更新),如果不是我们设置值。
对我来说意义不大,但它是:
public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) where T : IAuditable
{
foreach (var item in pairData)
{
var member = item.Key;
// If member type is a reference type, then member.Body is the property accessor.
// For value types it is Convert(property accessor)
var memberBody = member.Body as MemberExpression ?? (MemberExpression)((UnaryExpression)member.Body).Operand;
var lambda = Expression.Lambda<Action<T>>(Expression.Assign(memberBody, Expression.Constant(item.Value)), member.Parameters);
var action = lambda.Compile();
action(data);
}
}
UPDATE 根据评论中的要求,支持嵌套 class 属性 初始化的版本:
public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) //where T : IAuditable
{
var assignments = new List<Expression>();
foreach (var item in pairData)
{
var member = item.Key;
// If member type is a reference type, then member.Body is the property accessor.
// For value types it is Convert(property accessor)
var memberBody = member.Body as MemberExpression ?? (MemberExpression)((UnaryExpression)member.Body).Operand;
assignments.Clear();
assignments.Add(Expression.Assign(memberBody, Expression.Constant(item.Value)));
var target = member.Parameters[0];
while (memberBody.Expression != target)
{
var childMember = (MemberExpression)memberBody.Expression;
assignments.Add(Expression.IfThen(Expression.ReferenceEqual(childMember, Expression.Constant(null)),
Expression.Assign(childMember, Expression.New(childMember.Type))));
memberBody = childMember;
}
assignments.Reverse();
var body = assignments.Count > 1 ? Expression.Block(assignments) : assignments[0];
var lambda = Expression.Lambda<Action<T>>(body, target);
var action = lambda.Compile();
action(data);
}
}