Каковы некоторые примеры выражений MemberBinding LINQ? - PullRequest
13 голосов
/ 27 мая 2010

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

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

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

См. Также эту статью MSDN .

1 Ответ

28 голосов
/ 27 мая 2010

РЕДАКТИРОВАТЬ Заменяет предыдущий ответ в ответ на первый комментарий.

Классы, которые я использую в этих примерах, следующие:

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

Ну вот, я думаю, это должно охватить это.

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

...