使用lambda表达式的嵌套集合以创建一个对象图(Using nested collections

2019-09-03 13:20发布

我感兴趣的使用lambda表达式创建属性选择的树。

使用场景是,我们有一些代码,不会对对象图一些回归反射,而且,我们目前正在使用的属性标记的属性应遍历的递归限制的范围。 即获取对象的所有装饰性,如果该属性是引用类型与装饰性,重复每个那些太。

使用属性的限制是,你只能把它们放在你的类型控制源。 lambda表达式树允许在任意类型的公共成员范围之内。

这将是很方便有一个速记的方式来定义这些表达,它反映了对象图的结构。

最后,我很想有这样的事情:

Selector<MyType> selector = new [] {
        (t => Property1),
        (t => Property2)
        {
                p => NestedProperty1,
                p => NestedProperty2
        }
};

现在,我能做的最好的声明为每个节点明确这样的一个实例:

var selector = new Selector<MyType>()
{
    new SelectorNode<MyType, Property1Type>(t => Property1),
    new SelectorNode<MyType, Property2Type>(t => Property2)
    {
        new SelectorNode<Property2Type, NestedProperty1Type>(p => NestedProperty1),
        new SelectorNode<Property2Type, NestedProperty2Type>(p => NestedProperty2)
    },
};

有没有错码的,但你必须明确地写出每个节点的类型参数,因为编译器不能推断类型参数。 这是一种痛苦。 和丑陋。 我已经看到了一些令人难以置信的语法糖在那里,我相信一定有更好的办法。

由于我缺乏像动态,CO /逆变仿制药和表达式树“高” C#概念的理解,我想我会场上的问题在那里,看看有没有大师知道的方式来实现这一目标(或某事而不是像它?)


作为参考,这些都为声明SelectorSelectorNode是实现我在帖子中描述的结构类:

public interface ISelectorNode<T> {}

public class Selector<T>: List<ISelectorNode<T>>{}

public class SelectorNode<T, TOut>: List<ISelectorNode<TOut>>, ISelectorNode<T> 
{
    public SelectorNode(Expression<Func<T, TOut>> select) {}
}

//Examples of Usage below

public class Dummy
{
    public ChildDummy Child { get; set; }
}

public class ChildDummy
{
    public string FakeProperty { get; set; }
}

public class Usage
{
    public Usage()
    {
        var selector = new Selector<Dummy>
        {
            new SelectorNode<Dummy, ChildDummy>(m => m.Child)
            {
                new SelectorNode<ChildDummy, string>(m => m.FakeProperty)
            }
        };
    }
}

编辑在扩大纳瓦尔上的回答的兴趣:

利用C#的集合初始化语法,我们可以得到的代码如下所示:

var selector = new Selector<Dummy>
  {
      (m => m.Child),
      {dummy => dummy.Child, 
          c => c.FakeProperty,
          c => c.FakeProperty                    
      }
  };

这是如果我们的SelectorNode类的添加方法如下所示:

public class Selector<T> : List<ISelectorNode<T>>
{
    public SelectorNode<T, T, TOut> Add<TOut>(Expression<Func<T, TOut>> selector, params Expression<Func<TOut, object>>[] children)
    {
        return SelectorNode<T, T, TOut>.Add(this, this, selector);
    }
}

必须有利用这种语法的方式!

Answer 1:

我在这个阶段,我已经变得麻木思维太多选择承认,希望这是我最后一次.. :)

最后,你已经在你的问题中提到的一个- Expression<Func<T, object>>路线。 我不知道我怎样才能更好地这又不失一些编译时的安全性。 非常相似,我的第一个答案:

public class Selector<T> : List<ISelectorNode<T>>
{
    public static SelectorNode<T, TOut> Get<TOut>(Expression<Func<T, TOut>> selector)
    {
        return new SelectorNode<T, TOut>(selector);
    }

    public void Add<TOut>(Expression<Func<T, TOut>> selector)
    {
        var node = new SelectorNode<T, TOut>(selector);
        Add(node);
    }
}



public class SelectorNode<T, TOut> : List<ISelectorNode<TOut>>, ISelectorNode<T>
{

    public SelectorNode(Expression<Func<T, TOut>> selector)
    {

    }

    public ISelectorNode<T> Add(params Expression<Func<TOut, object>>[] selectors)
    {
        foreach (var selector in selectors)
            base.Add(new SelectorNode<TOut, object>(selector));

        return this;
    }

    public ISelectorNode<T> Add(params ISelectorNode<TOut>[] nodes)
    {
        AddRange(nodes);
        return this;
    }
}

而你拨打:

var selector = new Selector<Person>
{
    Selector<Person>.Get(m => m.Address).Add
    (
        Selector<Address>.Get(x => x.Place),
        Selector<Address>.Get(x => x.ParentName).Add
        (
            x => x.Id,
            x => x.FirstName,
            x => x.Surname
        )
    ),

    Selector<Person>.Get(m => m.Name).Add
    (
        x => x.Id,
        x => x.FirstName,
        x => x.Surname
    ),

    m => m.Age
};

这一切是我的最爱到现在为止(如果提供)..



Answer 2:

编辑:我的下面的答案不可原谅不回答这个问题。 我有点误读了。 我将提供另一种答案,其实可以做的工作。 保持这个答案开放的,因为它可以帮助别人的未来有关的东西。


这是你可以用流畅的界面来管理,但可能不会作出削减为您服务。

让你的选择类是这样的:

public class Selector<T> : List<ISelectorNode<T>>
{
    public SelectorNode<T, TOut> Add<TOut>(Expression<Func<T, TOut>> selector)
    {
        return SelectorNode<T, TOut>.Add(this, selector);
    }
}



public class SelectorNode<T, TOut> : List<ISelectorNode<TOut>>, ISelectorNode<T>
{
    //move this common functionality to a third static class if it warrants.
    internal static SelectorNode<T, TOut> Add(List<ISelectorNode<T>> list, Expression<Func<T, TOut>> selector)
    {
        var node = new SelectorNode<T, TOut>(selector);
        list.Add(node);
        return node;
    }



    SelectorNode(Expression<Func<T, TOut>> selector) //unhide if you want it.
    {

    }



    public SelectorNode<TOut, TNextOut> Add<TNextOut>(Expression<Func<TOut, TNextOut>> selector)
    {
        return SelectorNode<TOut, TNextOut>.Add(this, selector);
    }
}

现在,您可以拨打:

var selector = new Selector<Dummy>();
selector.Add(m => m.Child).Add(m => m.FakeProperty); //just chain the rest..

我个人觉得这不是在质疑你的方法更具有可读性,但并不像直观的或怪异:)我不认为你可以把它在同一行(可悲的是:(),但有可能是一个艰辛的道路。

更新:

一个班轮:

public class Selector<T> : List<ISelectorNode<T>>
{
    public SelectorNode<T, T, TOut> Add<TOut>(Expression<Func<T, TOut>> selector)
    {
        return SelectorNode<T, T, TOut>.Add(this, this, selector);
    }
}



public class SelectorNode<S, T, TOut> : List<ISelectorNode<TOut>>, ISelectorNode<T>
{
    //move this common functionality to a third static class if it warrants.
    internal static SelectorNode<S, T, TOut> Add(Selector<S> parent, List<ISelectorNode<T>> list, 
                                                 Expression<Func<T, TOut>> selector)
    {
        var node = new SelectorNode<S, T, TOut>(parent, selector);
        list.Add(node);
        return node;
    }



    Selector<S> parent;

    SelectorNode(Selector<S> parent, Expression<Func<T, TOut>> selector) //unhide if you want it.
    {
        this.parent = parent;
    }



    public SelectorNode<S, TOut, TNextOut> Add<TNextOut>(Expression<Func<TOut, TNextOut>> selector)
    {
        return SelectorNode<S, TOut, TNextOut>.Add(parent, this, selector);
    }

    public Selector<S> Finish()
    {
        return parent;
    }
}

用法:

var selector = new Selector<Dummy>().Add(m => m.Child).Add(m => m.FakeProperty).Finish();

//or the earlier

var selector = new Selector<Dummy>();
selector.Add(m => m.Child).Add(m => m.FakeProperty); //just chain the rest, no need of Finish

第一种方法的优点:

  1. 更简单

  2. 不改变现有定义(的SelectorNode

第二优势:

  1. 提供一个清洁的呼叫。

这两种方法的缺点少可能是因为现在你有一个内部的静态方法Add用于共享具有这两种选择课后没有意义的常用功能,但是这是宜居带我猜。 您可以删除的方法和重复的代码(或硬盘的方式,巢SelectorNodeSelector和隐藏实施,外界如果SelectorNode有外没有任何意义Selector类,或者甚至更糟,使其保护,并从其他继承一个类)

一个建议:你也许最想往的组成方式比你的继承方式List<T>秒。 你的类名(选择)不给它下一个集合的概念。 问得好顺便说一句!



Answer 3:

你的实际执行是非常干净和可读性,可能会有点详细为你喜欢的-从集合初始糖实例化集合实例(用时仅适用的事实,问题源于new的课程构造关键字)和可悲的是C#不推断构造类型 。 既然排除了,你现在要做的,至少在一定程度上是什么。

和语法像这样

(m => m.Child)
    .SomeAddMethod(c => c.FakeProperty)

不工作没有明确说明什么拉姆达实际上代表的 ,即使你有扩展方法SomeAddMethodExpression<Func<T, TOut>> 。 我不得不说这是有时皮塔。

什么是可以做到的是尽量减少型规格。 最常见的方法是创建一个静态类,它会要求你提供正式参数类型(在你的情况T )和一次正式的参数类型是已知的返回类型( TOut )将被推断从参数Expression<Func<T, TOut>>

让我们一步一步来。 考虑一个比较复杂的类层次结构:

public class Person
{
    public Address Address { get; set; }
    public Name Name { get; set; }
    public int Age { get; set; }
}

public class Address
{
    public string Place { get; set; }
    public Name ParentName { get; set; }
}

public class Name
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string Surname { get; set; }
}

假设你有这个(简单的):

public class Selector<T> : List<ISelectorNode<T>>
{
    public static SelectorNode<T, TOut> Get<TOut>(Expression<Func<T, TOut>> selector)
    {
        return new SelectorNode<T, TOut>(selector);
    }
}



public class SelectorNode<T, TOut> : List<ISelectorNode<TOut>>, ISelectorNode<T>
{
    internal SelectorNode(Expression<Func<T, TOut>> selector)
    {

    }
}

现在,您可以在手动但要少得多参数输入添加所有。 事情是这样的:

var selector = new Selector<Person>();

var pA = Selector<Person>.Get(m => m.Address);
    var aS = Selector<Address>.Get(m => m.Place);
    var aN = Selector<Address>.Get(m => m.ParentName);
        var nI1 = Selector<Name>.Get(m => m.Id);
        var nS11 = Selector<Name>.Get(m => m.FirstName);
        var nS12 = Selector<Name>.Get(m => m.Surname);

var pN = Selector<Person>.Get(m => m.Name);
    var nI2 = Selector<Name>.Get(m => m.Id);
    var nS21 = Selector<Name>.Get(m => m.FirstName);
    var nS22 = Selector<Name>.Get(m => m.Surname);

var pI = Selector<Person>.Get(m => m.Age);

selector.Add(pA);
    pA.Add(aS);
    pA.Add(aN);
        aN.Add(nI1);
        aN.Add(nS11);
        aN.Add(nS12);

selector.Add(pN);
    pN.Add(nI2);
    pN.Add(nS21);
    pN.Add(nS22);

selector.Add(pI);

很简单,但并不像直观的(我想任何一天你原来的语法这一点)。 可能是我们可以减少这个短:

public class Selector<T> : List<ISelectorNode<T>>
{
    public static SelectorNode<T, TOut> Get<TOut>(Expression<Func<T, TOut>> selector)
    {
        return new SelectorNode<T, TOut>(selector);
    }

    public Selector<T> Add(params ISelectorNode<T>[] nodes)
    {
        AddRange(nodes);
        return this;
    }
}



public class SelectorNode<T, TOut> : List<ISelectorNode<TOut>>, ISelectorNode<T>
{
    internal SelectorNode(Expression<Func<T, TOut>> selector)
    {

    }



    public ISelectorNode<T> Add(params ISelectorNode<TOut>[] nodes)
    {
        AddRange(nodes);
        return this;
    }
}

现在,您可以拨打:

var selector = new Selector<Person>().Add
(
    Selector<Person>.Get(m => m.Address).Add
    (
        Selector<Address>.Get(x => x.Place),
        Selector<Address>.Get(x => x.ParentName).Add
        (
            Selector<Name>.Get(x => x.Id),
            Selector<Name>.Get(x => x.FirstName),
            Selector<Name>.Get(x => x.Surname)
        )
    ), 
    Selector<Person>.Get(m => m.Name).Add
    (
        Selector<Name>.Get(x => x.Id),
        Selector<Name>.Get(x => x.FirstName),
        Selector<Name>.Get(x => x.Surname)
    ),
    Selector<Person>.Get(m => m.Age)
);

干净多了,但我们可以用集合初始化语法,使它看起来稍微好一点。 不需Add(params)在方法Selector<T>你会得到:

public class Selector<T> : List<ISelectorNode<T>>
{
    public static SelectorNode<T, TOut> Get<TOut>(Expression<Func<T, TOut>> selector)
    {
        return new SelectorNode<T, TOut>(selector);
    }
}

var selector = new Selector<Person>
{
    Selector<Person>.Get(m => m.Address).Add
    (
        Selector<Address>.Get(x => x.Place),
        Selector<Address>.Get(x => x.ParentName).Add
        (
            Selector<Name>.Get(x => x.Id),
            Selector<Name>.Get(x => x.FirstName),
            Selector<Name>.Get(x => x.Surname)
        )
    ),
    Selector<Person>.Get(m => m.Name).Add
    (
            Selector<Name>.Get(x => x.Id),
        Selector<Name>.Get(x => x.FirstName),
        Selector<Name>.Get(x => x.Surname)
    ),
    Selector<Person>.Get(m => m.Age)
};

通过具有其他Add在超负荷Selector<T>象下面这样可以最大限度地减少一些打字,但是这太疯狂了:

public class Selector<T> : List<ISelectorNode<T>>
{
    public static SelectorNode<T, TOut> Get<TOut>(Expression<Func<T, TOut>> selector)
    {
        return new SelectorNode<T, TOut>(selector);
    }

    public void Add<TOut>(Expression<Func<T, TOut>> selector)
    {
        var node = new SelectorNode<T, TOut>(selector);
        Add(node);
    }
}

var selector = new Selector<Person>
{
    Selector<Person>.Get(m => m.Address).Add
    (
        Selector<Address>.Get(x => x.Place),
        Selector<Address>.Get(x => x.ParentName).Add
        (
            Selector<Name>.Get(x => x.Id),
            Selector<Name>.Get(x => x.FirstName),
            Selector<Name>.Get(x => x.Surname)
        )
    ),

    Selector<Person>.Get(m => m.Name).Add
    (
        Selector<Name>.Get(x => x.Id),
        Selector<Name>.Get(x => x.FirstName),
        Selector<Name>.Get(x => x.Surname)
    ),

    m => m.Age // <- the change here
};

这工作,因为集合初始化可以调用不同的Add过载。 但我个人更喜欢以前调用的一贯作风。



Answer 4:

一些更(集合初始化)糖烂摊子:

public class Selector<T> : List<ISelectorNode<T>>
{
    public void Add(params Selector<T>[] selectors)
    {
        Add(this, selectors);
    }

    static void Add<TOut>(List<ISelectorNode<TOut>> nodes, Selector<TOut>[] selectors)
    {
        foreach (var selector in selectors)
            nodes.AddRange(selector);

        //or just, Array.ForEach(selectors, nodes.AddRange);
    }

    public void Add<TOut>(Expression<Func<T, TOut>> selector)
    {
        var node = new SelectorNode<T, TOut>(selector);
        Add(node);
    }

    //better to have a different name than 'Add' in cases of T == TOut collision - when classes 
    //have properties of its own type, eg Type.BaseType
    public Selector<T> InnerAdd<TOut>(params Selector<TOut>[] selectors)
    {
        foreach (SelectorNode<T, TOut> node in this)
            Add(node, selectors);

        //or just, ForEach(node => Add((SelectorNode<T, TOut>)node, selectors));
        return this;
    }
}

public class SelectorNode<T, TOut> : List<ISelectorNode<TOut>>, ISelectorNode<T>
{
    internal SelectorNode(Expression<Func<T, TOut>> selector)
    {

    }
}

现在这样称呼它:

var selector = new Selector<Person>
{
    new Selector<Person>
    {
        m => m.Address
    }.InnerAdd
    (
        new Selector<Address>
        {
            n => n.Place
        },
        new Selector<Address>
        {
            n => n.ParentName
        }.InnerAdd
        (
            new Selector<Name>
            {
                o => o.Id,
                o => o.FirstName,
                o => o.Surname
            }
        )
    ),

    new Selector<Person>
    {
        m => m.Name
    }.InnerAdd
    (
        new Selector<Name>
        {
            n => n.Id,
            n => n.FirstName,
            n => n.Surname
        }
    ),

    m => m.Age
};

这是否帮助? 我不认为如此。 很多令人讨厌,但很少直观。 更糟的是,不存在固有的类型安全(这一切都依赖于你提供什么类型的Selector<T>集合初始化)。



Answer 5:

还有一个 - 没有类型规范可言,但难看:)

static class Selector
{
    //just a mechanism to share code. inline yourself if this is too much abstraction
    internal static S Add<R, S, T, TOut>(R list, Expression<Func<T, TOut>> selector,
                                         Func<SelectorNode<T, TOut>, S> returner) where R : List<ISelectorNode<T>>
    {
        var node = new SelectorNode<T, TOut>(selector);
        list.Add(node);
        return returner(node);
    }
}



public class Selector<T> : List<ISelectorNode<T>>
{
    public Selector<T> AddToConcatRest<TOut>(Expression<Func<T, TOut>> selector)
    {
        return Selector.Add(this, selector, node => this);
    }

    public SelectorNode<T, TOut> AddToAddToItsInner<TOut>(Expression<Func<T, TOut>> selector)
    {
        return Selector.Add(this, selector, node => node);
    }
}



public class SelectorNode<T, TOut> : List<ISelectorNode<TOut>>, ISelectorNode<T>
{
    internal SelectorNode(Expression<Func<T, TOut>> selector)
    {

    }



    public SelectorNode<T, TOut> InnerAddToConcatRest<TNextOut>(Expression<Func<TOut, TNextOut>> selector)
    {
        return AddToConcatRest(selector);
    }

    public SelectorNode<TOut, TNextOut> InnerAddToAddToItsInnerAgain<TNextOut>(Expression<Func<TOut, TNextOut>> selector)
    {
        return AddToAddToItsInner(selector);
    }

    //or just 'Concat' ?
    public SelectorNode<T, TOut> AddToConcatRest<TNextOut>(Expression<Func<TOut, TNextOut>> selector)
    {
        return Selector.Add(this, selector, node => this);
    }

    public SelectorNode<TOut, TNextOut> AddToAddToItsInner<TNextOut>(Expression<Func<TOut, TNextOut>> selector)
    {
        return Selector.Add(this, selector, node => node);
    }
}

我已经给描述性名称的功能,使意图明确。 我不打算详细什么个别的呢,我猜的函数名就足以说明。 去通过前面的例子:

var selector = new Selector<Person>();

var pA = selector.AddToAddToItsInner(m => m.Address);
    var aN = pA.InnerAddToConcatRest(m => m.Place);
    var aS = aN.AddToAddToItsInner(m => m.ParentName);
        var nI1 = aS.InnerAddToConcatRest(m => m.Id);
        var nS11 = nI1.AddToConcatRest(m => m.FirstName);
        var nS12 = nS11.AddToConcatRest(m => m.Surname);

var pN = selector.AddToAddToItsInner(m => m.Name);
    var nI2 = pN.InnerAddToConcatRest(m => m.Id);
    var nS21 = nI2.AddToConcatRest(m => m.FirstName);
    var nS22 = nS21.AddToConcatRest(m => m.Surname);

var pI = selector.AddToConcatRest(m => m.Age);

或者给出一个替代(开车的念头家):

var selector = new Selector<Person>();

var pA = selector.AddToAddToItsInner(m => m.Address);
    var aS = pA.InnerAddToConcatRest(m => m.Place);
    var aN = pA.InnerAddToAddToItsInnerAgain(m => m.ParentName);
        var nI1 = aN.InnerAddToConcatRest(m => m.Id);
        var nS11 = nI1.AddToConcatRest(m => m.FirstName);
        var nS12 = nS11.AddToConcatRest(m => m.Surname);

var pN = selector.AddToAddToItsInner(m => m.Name);
    var nI2 = pN.InnerAddToConcatRest(m => m.Id);
    var nS21 = nI2.AddToConcatRest(m => m.FirstName);
    var nS22 = nS21.AddToConcatRest(m => m.Surname);

var pI = selector.AddToConcatRest(m => m.Age);

现在,我们可以结合起来,使之简洁,省去了多余的变量:

var selector = new Selector<Person>();

selector.AddToConcatRest(m => m.Age).AddToAddToItsInner(m => m.Address)
            .InnerAddToConcatRest(m => m.Place).AddToAddToItsInner(m => m.ParentName)
                .InnerAddToConcatRest(m => m.Id).AddToConcatRest(m => m.FirstName).AddToConcatRest(m => m.Surname);

selector.AddToAddToItsInner(m => m.Name)
            .InnerAddToConcatRest(m => m.Id).AddToConcatRest(m => m.FirstName).AddToConcatRest(m => m.Surname);

现在你可能已经注意到,很多这些的Add功能在内部做同样的工作。 我分开的方法,因为从发送方他们有不同的语义来执行。 如果你能知道它在做什么/意味着然后代码可以更短一次。 变化SelectorNode<,>类:

public class SelectorNode<T, TOut> : List<ISelectorNode<TOut>>, ISelectorNode<T>
{
    internal SelectorNode(Expression<Func<T, TOut>> selector)
    {

    }



    public SelectorNode<T, TOut> Add<TNextOut>(Expression<Func<TOut, TNextOut>> selector)
    {
        return Selector.Add(this, selector, node => this);
    }

    public SelectorNode<TOut, TNextOut> AddToAddToItsInner<TNextOut>(Expression<Func<TOut, TNextOut>> selector)
    {
        return Selector.Add(this, selector, node => node);
    }
}

现在使用:

var selector = new Selector<Person>();
selector.AddToConcatRest(m => m.Age).AddToAddToItsInner(m => m.Address)
            .Add(m => m.Place).AddToAddToItsInner(m => m.ParentName)
                .Add(m => m.Id).Add(m => m.FirstName).Add(m => m.Surname);
selector.AddToAddToItsInner(m => m.Name)
            .Add(m => m.Id).Add(m => m.FirstName).Add(m => m.Surname);

也许有很多可能的ESP其他替代品,当你去的各种方法的组合。 在方法链接的此特定情况下,如果该呼叫者混淆,另一种可能性是一味从呼叫者侧添加并在内部丢弃所述重复。 事情是这样的:

var selector = new Selector<Person>();
selector.Add(m => m.Address).Add(m => m.Place);
selector.Add(m => m.Address).Add(m => m.ParentName).Add(m => m.Id); //at this stage discard duplicates
selector.Add(m => m.Address).Add(m => m.ParentName).Add(m => m.FirstName); //and so on
selector.Add(m => m.Name)... etc
selector.Add(m => m.Age);

为此,您将要介绍的节点类所有这使得它非常脆弱的自己相等比较。

另一直观的方法将是直接链中的属性的表达。 喜欢:

selector.Add(m => m.Address.Place);
selector.Add(m => m.Address.ParentName.Id);
selector.Add(m => m.Address.ParentName.FirstName); // and so on.

在内部,你会需要打破表达降至件,建立基于他们自己的表达。 如果我有时间,我会做出一些以后阶段的答案。



Answer 6:

有一件事我要问你的是,为什么不使用反射,并保存自己从提供参数的麻烦? 您可以使用递归通过节点(属性)遍历并手动建立从那里树(见线程本或本 )。 这可能不会给你,你或许会想要的那种灵活性,虽然。

表达是不是我的强项,所以以此为伪代码。 当然,你将有更多的工作需要做。

public class Selector<T> : List<ISelectorNode<object>>
{
    public Selector()
    {
        Add(typeof(T), this);
    }

    void Add(Type type, List<ISelectorNode<object>> nodes)
    {
        foreach (var property in type.GetProperties()) //with whatever flags
        {
            //the second argument is a cool param name I have given, discard-able 
            var paramExpr = Expression.Parameter(type, type.Name[0].ToString().ToLower()); 
            var propExpr = Expression.Property(paramExpr, property);

            var innerNode = new SelectorNode(Expression.Lambda(propExpr, paramExpr));
            nodes.Add(innerNode);
            Add(property.PropertyType, innerNode);
        }
    }
}



public class SelectorNode : List<ISelectorNode<object>>, ISelectorNode<object>
{

    internal SelectorNode(LambdaExpression selector)
    {

    }
}

与用法:

var selector = new Selector<Person>();

只是仅此而已。 这将产生你可能不希望这些属性,如性能上内置的类型,如DateTimestring等,但我认为它的琐碎绕过它们。 或者更好的,你可以创建自己的规则,并通过他们来确定遍历应该如何发生。



文章来源: Using nested collections of lambda expressions to create an object graph