【问题标题】:Assign value to object using Expression使用表达式为对象赋值
【发布时间】:2015-10-15 09:32:13
【问题描述】:

我有这个注入器,它使用表达式注入数据

    /// <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);

        }
    }

我只传递一个表达式和值的集合。

在哪里赋值的表达式(例如a => a.Code)和 值(例如“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

提前致谢

任何帮助将不胜感激。

【问题讨论】:

标签: c# lambda expression


【解决方案1】:

对我来说没有太大意义,但这里是:

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);
    }
}

更新根据评论中的要求,支持嵌套类属性初始化的版本:

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);
    }
}

【讨论】:

  • 嗨,Ivan,感谢您的回复,它似乎在这里工作,我唯一的问题是它是嵌套属性时。 a => a.Employment.Code.. 我知道抛出未设置对象实例的对象引用,因为就业未实例化。如果它会自动实例化,是否有可能?
  • @EntengDagpin 可以,但不像上面的sn-p那么简单。
  • 我将从这个 sn-p 开始我的解决方案。谢谢@ivan
  • 嗨@IvanStoev,在我看到你更新的答案之前,我尝试了一些递归 Func> 的解决方案来列出所有 member.Expression 并在类型为时迭代它类然后实例化它:D 谢谢,你的解决方案效果很好.. 干杯!
【解决方案2】:

您可以使用以下代码:

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;
}

请注意,这里我们使用比较器来检查当前值是否等于新值。如果是,我们只返回(无需更新),如果不是,我们设置值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-12
    • 1970-01-01
    • 2019-11-15
    • 2020-02-25
    • 1970-01-01
    • 2019-09-03
    • 1970-01-01
    相关资源
    最近更新 更多