РЕДАКТИРОВАТЬ Заменяет предыдущий ответ в ответ на первый комментарий.
Классы, которые я использую в этих примерах, следующие:
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
, передавая ConstructorInfo типа Node. Откройте выходную DLL в Reflector, чтобы понять, что я имею в виду.
Прежде всего я должен упомянуть, что эти три типа выражений, о которых вы спрашиваете, обычно передаются в массиве MemberBinding [] в Expression.New или встроены друг в друга (поскольку инициализаторы Member по своей природе рекурсивны).
На участке ...
MemberAssignment
Выражение MemberAssignment представляет настройку одного члена нового экземпляра с возвращаемым значением данного выражения. Он создается в коде с использованием фабричного метода Expression.Bind
. Это наиболее часто встречаемое, и в коде C # это эквивалентно следующему:
new NodeData() { /* start */ Name = "hello" /* end */ };
или
new Node() { /* start */ Data = new NodeData() /* end */ };
MemberMemberBinding
MemberMemberBinding представляет собой встроенную инициализацию членов члена, который уже инициализирован (то есть newed, или структуры, которая в любом случае не может быть нулевой). Он создается через Expression.MemberBind
и не представляет создание нового экземпляра . Следовательно, он отличается от метода MemberBind тем, что не принимает ConstructorInfo, а ссылается на метод Get Property (метод доступа к свойству). В результате попытка инициализировать элемент таким образом, который начинается с нуля, приведет к исключению NullReferenceException.
Итак, чтобы сгенерировать это в коде, вы делаете это:
new Node() { /* start */ Data = { Name = "hello world" } /* end */};
Это может показаться немного странным, но здесь происходит то, что метод get свойства для Data
выполняется для получения ссылки на уже инициализированный член. Имея это в виду, внутренние MemberBindings затем выполняются по очереди, так что по сути вышеприведенный код не перезаписывает данные, а делает это:
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 =
является привязкой MemberMember.
Надеюсь, это понятно!
MemberListBinding
Созданный методом Expression.ListBind
(требующий также вызова Expression.ElementInit
), он аналогичен MemberMemberBinding (в том смысле, что элемент объекта не создается заново) , но на этот раз это экземпляр 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);
}
Ну вот, я думаю, это должно охватить это.
Другое дело, поддерживаете ли вы их в своем коде! ;)