MemberBinding LINQ表达式的一些示例是什么?
有三种可能性,但我找不到例子:
- System.Linq.Expressions.MemberAssignment
- System.Linq.Expressions.MemberListBinding
- System.Linq.Expressions.MemberMemberBinding
我想写一些unit testing,看看我是否可以处理它们,但我不知道怎么写它们除了第一个,它似乎是new Foo { Property = "value" }
其中Property =“value”是MemberAssignment
类型的MemberAssignment
。
另请参阅此MSDN文章 。
编辑这将取代之前的答案以回应第一条评论。
我在这些例子中使用的类如下:
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 _children = new List () { null }; public IList 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> = () => new Node();
将生成一个内联表达式,其中包含对Expression.New
的调用,并传递Node类型的ConstructorInfo。 在Reflector中打开输出DLL以查看我的意思。
我首先要提到的是,您询问的这三种表达式类型通常在Expression.New中的MemberBinding []数组中传递,或者相互嵌入(因为成员初始化器本身就是递归的)。
关于情节……
MemberAssignment
MemberAssignment表达式表示具有给定表达式的返回值的新实例的单个成员的设置 。 它是使用Expression.Bind
工厂方法在代码中生成的。 这是您将看到的最常见的,在C#代码中,这相当于以下内容:
new NodeData() { /* start */ Name = "hello" /* end */ };
要么
new Node() { /* start */ Data = new NodeData() /* end */ };
MemberMemberBinding
MemberMemberBinding表示已初始化的成员(即newed,或无论如何不能为null的结构)的内联初始化。 它是通过Expression.MemberBind
创建的, 并不代表创建新实例 。 因此,它与MemberBind方法的不同之处在于不采用ConstructorInfo,而是引用Property Get方法(属性访问器)。 因此,尝试以这种方式初始化成员以null开始将导致NullReferenceException。
因此,要在代码中生成此代码,请执行以下操作:
new Node() { /* start */ Data = { Name = "hello world" } /* end */};
这可能看起来有点奇怪,但这里发生的是正在执行Data
的属性get方法以获取对已经初始化的成员的引用。 有了这个,然后内部的MemberBindings依次执行,所以上面的代码有效地不会覆盖Data,但这样做:
new Node().Data.Name = "hello world";
这就是为什么需要这个表达式类型的原因,因为如果你必须设置多个属性值,你不能在一个单行中执行它,除非有一些特殊的表达式/语法来执行它。 如果NodeData
有另一个你想同时设置的字符串成员( NodeData
),没有初始化语法/表达式,你必须这样做:
var node = new Node(); node.Data.Name = "first"; node.Data.OtherName = "second";
这不是一个class轮 – 但这是:
var node = new Node() { Data = { Name = "first", OtherName="second" } };
Data =
bit的成员是MemberMemberBinding。
我希望这很清楚!
MemberListBinding
由Expression.ListBind
方法创建(也需要调用Expression.ElementInit
),这类似于MemberMemberBinding(因为对象的成员不是重新创建的),但这一次,它是ICollection / IList的一个实例。 添加到内联元素:
new Node() { /* start */ Children = { new Node(), new Node() } /* end */ };
所以,最后两个表达式是有点边缘的情况,但肯定是你可能会遇到的事情,因为它们显然非常有用。
最后,我附上一个你可以运行的unit testing,它将certificate我对这些表达式的断言 – 如果你反映方法体,你会看到相关的工厂方法在我用注释突出显示的点上被调用块:
[TestMethod] public void TestMethod1() { Expression> e = () => new Node() { Data = new NodeData() }; Expression> e2 = () => new Node() { Data = { Name = "MemberMemberBinding" } }; Expression> 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); }
你去了,我认为应该覆盖它。
是否应该在代码中支持它们是另一回事! ;)