Quali sono alcuni esempi di espressioni LINQ di MemberBinding?

.net c# expression-trees linq

Domanda

Ci sono tre possibilità, ma non riesco a trovare esempi:

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

Voglio scrivere alcuni test unitari per vedere se riesco a gestirli, ma non so come scriverli tranne per il primo, che sembra essere il new Foo { Property = "value" } dove Property = "value" è un'espressione di tipo MemberAssignment .

Vedi anche questo articolo MSDN .

Risposta accettata

MODIFICA Sostituisce la risposta precedente in risposta al primo commento.

Le classi che sto utilizzando in questi esempi sono le seguenti:

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

In primo luogo, è possibile ottenere il compilatore C # per generare espressioni che consentano di esaminare il modo in cui funzionano di più effettuando le seguenti operazioni:

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

Genera un'espressione incorporata che contiene una chiamata a Expression.New , passando ConstructorInfo del tipo Node. Aprire la DLL di output in Reflector per vedere cosa intendo.

Prima di tutto devo menzionare che questi tre tipi di espressione che chiedi sono tipicamente passati in una matrice MemberBinding [] in un Expression.New o incorporati l'uno nell'altro (poiché gli inizializzatori dei membri sono intrinsecamente ricorsivi).

Sulla trama ...

MemberAssignment

L'espressione MemberAssignment rappresenta l' impostazione di un singolo membro di una nuova istanza con il valore di ritorno di una determinata espressione. Viene prodotto in codice utilizzando il metodo Expression.Bind factory. Questo è il più comune che vedrai e nel codice C # è equivalente al seguente:

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

o

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

MemberMemberBinding

MemberMemberBinding rappresenta l'inizializzazione inline dei membri di un membro che è già inizializzato (cioè newed o una struct che non può essere null in ogni caso). Viene creato tramite Expression.MemberBind e non rappresenta la creazione di una nuova istanza . Pertanto, si differenzia dal metodo MemberBind non prendendo un ConstructorInfo, ma un riferimento a un metodo Property Get (accesso proprietà). Di conseguenza, un tentativo di inizializzare un membro in questo modo che inizia con null risulterà in una NullReferenceException.

Quindi, per generare questo in codice lo fai:

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

Questo potrebbe sembrare un po 'strano, ma quello che sta accadendo qui è che il metodo get della proprietà per Data viene eseguito per ottenere un riferimento al membro già inizializzato. Con quello in mano, le MemberBind interne vengono quindi eseguite a turno, quindi il codice precedente non sovrascrive i dati, ma facendo ciò:

new Node().Data.Name = "hello world";

E questo è il motivo per cui è richiesto questo tipo di espressione, perché se devi impostare più valori di proprietà, non puoi farlo in un solo liner, a meno che non ci sia qualche espressione / sintassi speciale per farlo. Se NodeData avesse un altro membro di stringa ( OtherName ) che avresti voluto anche impostare allo stesso tempo, senza sintassi / espressioni di inizializzazione, dovresti fare questo:

var node = new Node();
node.Data.Name = "first";
node.Data.OtherName = "second";

Quale non è un solo rivestimento - ma questo è:

var node = new Node() { Data = { Name = "first", OtherName="second" } };

Dove Data = bit è MemberMemberBinding.

Spero sia chiaro!

MemberListBinding

Creato dal metodo Expression.ListBind (che richiede anche chiamate a Expression.ElementInit ), è simile a MemberMemberBinding (nel fatto che il membro di un oggetto non viene creato di nuovo), ma questa volta è un'istanza di ICollection / IList che viene aggiunto a con elementi in linea .:

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

Quindi, queste ultime due espressioni sono piuttosto dei casi limite, ma certamente sono cose che potresti facilmente incontrare, in quanto sono chiaramente molto utili.

Infine, allego un test unitario che puoi eseguire per dimostrare le asserzioni che applico su queste espressioni - e se rifletti sul corpo del metodo, vedrai che i metodi di fabbrica rilevanti vengono richiamati nei punti che evidenzia con il commento blocchi:

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

Ecco, penso che dovrebbe coprirlo.

Se dovresti sostenerli nel tuo codice è un'altra cosa! ;)



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché