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

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

  1. Система. Linq. Выражения. MemberAssignment
  2. Система. Linq. Выражения. MemberListBinding
  3. Система. Linq. Выражения. MemberMemberBinding

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

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

13
задан Michiel van Oosterhout 8 December 2011 в 23:47
поделиться

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

Во-первых, вы можете заставить компилятор 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 представляет собой встроенную инициализацию членов члена, который уже инициализирован (т.е. создан, или структура, которая в любом случае не может быть нулевой). Он создается через Expression.MemberBind и не представляет собой создание нового экземпляра. Поэтому он отличается от метода MemberBind тем, что принимает не ConstructorInfo, а ссылку на метод Property Get (аксессор свойства). В результате попытка инициализировать таким образом член, который начинается с null, приведет к NullReferenceException.

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

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

Это может показаться немного странным, но здесь происходит то, что метод property 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 */ };

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

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

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

Вот так, я думаю, это должно покрыть все.

Стоит ли поддерживать их в своем коде - это другой вопрос! ;)

.
29
ответ дан 1 December 2019 в 20:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: