【问题标题】:What are some examples of MemberBinding LINQ expressions?MemberBinding LINQ 表达式有哪些示例?
【发布时间】:2010-05-26 22:57:41
【问题描述】:

有三种可能,但我找不到例子:

  1. System.Linq.Expressions.MemberAssignment
  2. System.Linq.Expressions.MemberListBinding
  3. System.Linq.Expressions.MemberMemberBinding

想写一些单元测试看看能不能处理,但是除了第一个不知道怎么写,好像是new Foo { Property = "value" } where Property = "value" MemberAssignment 类型的表达式。

另见MSDN article

【问题讨论】:

    标签: c# .net linq expression-trees


    【解决方案1】:

    我在这些示例中使用的类如下:

    public class Node
    {
      //initialise non-null, so we can use the MemberMemberBinding
      private NodeData _data = new NodeData();
      public NodeData Data { get { return _data; } set { _data = value; } }
      //initialise with one element so you can see how a MemberListBind
      //actually adds elements to those in a list, not creating it new.
      //Note - can't set the element to 'new Node()' as we get a Stack Overflow!
      private IList<Node> _children = new List<Node>() { null };
      public IList<Node> Children 
        { get { return _children; } set { _children = value; } }
    }
    
    public class NodeData
    {
      private static int _counter = 0;
      //allows us to count the number of instances being created.
      public readonly int ID = ++_counter;
      public string Name { get; set; }
    }
    

    首先,您可以让 C# 编译器生成表达式,以便您通过执行以下操作来研究它们是如何工作的:

    Expression<Func<Node>> = () => new Node();
    

    将生成一个内联表达式,其中包含对 Expression.New 的调用,并传递 Node 类型的 ConstructorInfo。在 Reflector 中打开输出 DLL 看看我的意思。

    我首先应该提到,您询问的这三种表达式类型通常在 Expression.New 中的 MemberBinding[] 数组中传递,或者相互嵌入(因为 Member 初始化程序本质上是递归的)。

    进入剧情……

    成员分配

    MemberAssignment 表达式表示具有给定表达式返回值的新实例的单个成员的设置。它是使用Expression.Bind 工厂方法在代码中生成的。这是您将看到的最常见的情况,在 C# 代码中这等价于以下内容:

    new NodeData() { /* start */ Name = "hello" /* end */ };
    

    new Node() { /* start */ Data = new NodeData() /* end */ };
    

    会员会员绑定

    MemberMemberBinding 表示已经初始化的成员的成员的内联初始化(即新的,或者无论如何都不能为空的结构)。它是通过Expression.MemberBind 创建的,不代表创建新实例。因此,它与 MemberBind 方法的不同之处在于它不采用 ConstructorInfo,而是对 Property Get 方法(属性访问器)的引用。因此,尝试以这种方式初始化从 null 开始的成员将导致 NullReferenceException。

    所以,要在代码中生成这个,你可以这样做:

    new Node() { /* start */ Data = { Name = "hello world" } /* end */};
    

    这可能看起来有点奇怪,但这里发生的是 Data 的属性获取方法正在执行以获取对已初始化成员的引用。有了这个,内部的 MemberBindings 就会依次执行,所以上面的代码实际上并没有覆盖 Data,而是这样做:

    new Node().Data.Name = "hello world";
    

    这就是为什么需要这种表达式类型的原因,因为如果你必须设置多个属性值,你不能在单行中做到这一点,除非有一些特殊的表达式/语法来做到这一点。如果 NodeData 有另一个字符串成员 (OtherName),您还想同时设置它,但没有初始化器语法/表达式,您必须这样做:

    var node = new Node();
    node.Data.Name = "first";
    node.Data.OtherName = "second";
    

    这不是一个单一的班轮 - 但这是:

    var node = new Node() { Data = { Name = "first", OtherName="second" } };
    

    Data = 位是 MemberMemberBinding。

    我希望这很清楚!

    成员列表绑定

    Expression.ListBind 方法创建(还需要调用Expression.ElementInit),这类似于MemberMemberBinding(因为对象的成员不是正在创建anew),但这一次,它是一个 ICollection/IList 的实例,它被添加到 中并带有内联元素。:

    new Node() { /* start */ Children = { new Node(), new Node() } /* end */ };
    

    所以,最后两个表达式有点极端,但肯定是你很可能遇到的事情,因为它们显然非常有用。

    最后,我附上了一个你可以运行的单元测试,它将证明我对这些表达式所做的断言 - 如果你反映方法体,你会看到相关的工厂方法在我强调的点被调用带有注释块:

    [TestMethod]
    public void TestMethod1()
    {
      Expression<Func<Node>> e = 
        () => new Node() { Data = new NodeData() };
    
      Expression<Func<Node>> e2 = 
        () => new Node() { Data = { Name = "MemberMemberBinding" } };
    
      Expression<Func<Node>> e3 = 
        () => new Node() { Children = { new Node(), new Node() } };
    
      var f = e.Compile();
      var f2 = e2.Compile();
      var f3 = e3.Compile();
    
      var node = f();
      //proves that this data was created anew as part of the expression.
      Assert.AreEqual(2, node.Data.ID);
      var node2 = f2();
      //proves that the data node's name was merely initialised, and that the
      //node data itself was not created anew within the expression.
      Assert.AreEqual(3, node2.Data.ID);
      Assert.AreEqual("MemberMemberBinding", node2.Data.Name);
      var node3 = f3();
      //count is three because the two elements in the MemberListBinding
      //merely added two to the existing first null item.
      Assert.AreEqual(3, node3.Children.Count);
    }
    

    你去吧,我认为应该涵盖它。

    您是否应该在代码中支持它们是另一回事! ;)

    【讨论】:

    • 哇...我什至不知道最后两个在 C# 中是合法的!
    • @luksan - 我在研究答案时也学到了很多东西!我可以说我还没有利用 c#/.net 表达式的这些特性,但是总有一天……
    猜你喜欢
    • 2011-11-22
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    相关资源
    最近更新 更多