Каковы некоторые примеры выражений MemberBinding LINQ?

Есть три возможности, но я не могу найти примеры:

  • System.Linq.Expressions.MemberAssignment
  • System.Linq.Expressions.MemberListBinding
  • System.Linq.Expressions.MemberMemberBinding

Я хочу написать некоторые модульные тесты, чтобы увидеть, могу ли я их обработать, но я не знаю, как их записать, кроме первого, который выглядит как new Foo { Property = "value" } где Свойство = "значение" - выражение типа MemberAssignment.

См. также статья MSDN.

Ответ 1

EDIT. Это заменяет предыдущий ответ в ответ на первый комментарий.

В следующих примерах я использую следующие классы:

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; }
}

Во-первых, вы можете заставить компилятор С# генерировать выражения для изучения того, как они работают больше, выполняя следующие действия:

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

Создает встроенное выражение, которое содержит вызов Expression.New, передавая конструкторInfo типа Node. Откройте выходную DLL в Reflector, чтобы увидеть, что я имею в виду.

Я должен сначала упомянуть, что эти три типа выражений, о которых вы спрашиваете, обычно передаются в массиве MemberBinding [] в Expression.New или встроены друг в друга (поскольку инициализаторы элементов по своей сути являются рекурсивными).

В сюжете...

MemberAssignment

Выражение MemberAssignment представляет параметр одного члена нового экземпляра с возвращаемым значением данного выражения. Он создается в коде с использованием метода Expression.Bind factory. Это наиболее часто встречающееся, что вы увидите, а в коде С# это эквивалентно следующему:

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

или

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

MemberMemberBinding

MemberMemberBinding представляет собой встроенную инициализацию членов члена, который уже инициализирован (то есть newed или struct, который в любом случае не может быть нулевым). Он создается с помощью Expression.MemberBind, а не представляет собой создание нового экземпляра. Поэтому он отличается от метода MemberBind, не принимая ConstructorInfo, а ссылается на метод Get Get (свойство accessor). В результате попытка инициализировать элемент таким образом, который начинается с нуля, приведет к исключению NullReferenceException.

Итак, чтобы сгенерировать это в коде, вы делаете это:

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

Это может показаться немного странным, но здесь происходит то, что метод get для 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.

Я надеюсь, что это ясно!

MemberListBinding

Созданный методом Expression.ListBind (требующий также вызовов Expression.ElementInit), это похоже на MemberMemberBinding (в том, что элемент объекта не создается заново), но на этот раз, это экземпляр ICollection/IList, который добавлен в со встроенными элементами.:

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

Итак, эти последние два выражения являются любопытными краевыми случаями, но, безусловно, это то, что вы могли бы встретить, так как они явно очень полезны.

Наконец, я прилагаю unit test, который вы можете запустить, что докажет утверждения, которые я делаю об этих выражениях, - и если вы отражаете тело метода, вы увидите, что соответствующие методы factory вызывают на пункты, которые я выделяю с помощью блоков комментариев:

[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 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);
}

Там вы идете, я думаю, что это должно было его покрыть.

Если вы должны поддерживать их в своем коде, это другое дело!;)