如何在属性上正确使用表达式树(无反射)

时间:2016-06-07 13:37:50

标签: c# generics lambda

目标:处理一个对象,如果该对象实现了一个期望的类型,我想更改一个特定的属性值(这部分工作正常),我也想应用相同的逻辑到具有相同预期类型的​​所有属性列表(我明确指出)。

我有以下代码:

public abstract class BaseObject
{
    public int Id { get; set; }
}

public class Father : BaseObject
{
    public DateTime CreatedOn { get; set; }
    public string Name { get; set; }
    public IEnumerable<ChildA> Children1 { get; set; }
    public IEnumerable<ChildB> Children2 { get; set; }
    public IEnumerable<ChildA> Children3 { get; set; }
    public IEnumerable<ChildB> Children4 { get; set; }
}

public class ChildA : BaseObject
{
    public int Val1 { get; set; }
}

public class ChildB : BaseObject
{
    public string Name { get; set; }
    public int Total { get; set; }
}

我想通过对目标对象的特定属性以及我明确说出的所有属性子项应用一些更改来处理对象:

public void Start()
{
    var listA = new List<ChildA> { new ChildA { Id = 1, Val1 = 1 }, new ChildA { Id = 2, Val1 = 2 } };
    var listB = new List<ChildB> { new ChildB { Id = 1, Name = "1", Total = 1 } };
    var obj = new Father { Id = 1, CreatedOn = DateTime.Now, Name = "F1", ChildrenA = listA, ChildrenB = listB };

    // I explicit tell to process only 2 of the 4 lists....
    ProcessObj(obj, x => new object[] { x.Children1, x.Children2 });            
}

我能写这个函数:

public void ProcessObj<T>(T obj, Expression<Func<T, object[]>> includes = null)
{
    var objBaseObject = obj as BaseObject;
    if (objBaseObject == null) return;

    // Here I change the ID - add 100 just as an example....
    objBaseObject.Id = objBaseObject.Id + 100;

    if (includes == null) return;

    var array = includes.Body as NewArrayExpression;
    if (array == null) return;

    var exps = ((IEnumerable<object>)array.Expressions).ToArray();
    for (var i = 0; i < exps.Count(); i++)
    {
        var name = ((MemberExpression)exps[i]).Member.Name;
        var childProperty = obj.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.Instance
               ).FirstOrDefault(prop => prop.Name == name);
        if (childProperty == null) continue;

        // NOT correct because I think I am getting a copy of the object 
        // and not pointing to the object in memory (by reference)
        var childList = childProperty.GetValue(obj); 

        // TODO: loop on the list and apply the same logic as the father.... 
        // change the ID field....
    }
}

在这个原型中我开始写反射,但我真的想尽可能避免它......

我该怎么办?

1 个答案:

答案 0 :(得分:3)

也许我错过了一些东西,但似乎你通过使用表达式树来解决问题。你能不能使用常规的Action和Func代表来做这件事吗?为什么他们需要表达树?这是一个只使用代表的例子:

public void ProcessObj<T>(T obj, Func<T, IEnumerable<object>> includes) {
     var objBaseObject = obj as BaseObject;
    if (objBaseObject == null) return;


    // Create a reusable action to use on both the parent and the children
    Action<BaseObject> action = x => x.Id += 100;

    // Run the action against the root object
    action(objBaseObject);

    // Get the includes by just invoking the delegate. No need for trees.
    var includes = includes(obj);

    // Loop over each item in each collection. If the types then invoke the same action that we used on the root. 
    foreach(IEnumerable<object> include in includes) 
    {
       foreach(object item in include) 
       {
          var childBaseObject = item as BaseObject;
          if(childBaseObject != null) 
          {
            action(childBaseObject);          
          }
       }
    }
}

就像以前一样可用:

ProcessObj(obj,x =&gt; new object [] {x.Children1,x.Children2});

没有表达式树和没有反射,只是常规委托lambda。

希望有所帮助