2013-04-01 12 views
32

volte ho bisogno di usare una tupla, per esempio io ho la lista dei carri armati e dei loro carri armati di destinazione (che inseguono dopo di loro o qualcosa di simile):C'è un trucco nella creazione di un elenco generico di tipo anonimo?

List<Tuple<Tank,Tank>> mylist = new List<Tuple<Tank,Tank>>(); 

e poi quando ho iterare la lista che ho accedervi da

mylist[i].item1 ... 
mylist[i].item2 ... 

e 'molto confusa e mi dimentico sempre ciò che è e ciò che è item1 item2, se potessi accedere da:

mylist[i].AttackingTank... 
mylist[i].TargetTank... 

sarebbe molto più chiaro, c'è un modo per farlo senza definire una classe:

MyTuple 
{ 
public Tank AttackingTank; 
public Tank TargetTank; 
} 

voglio evitare di definire questa classe, perché poi avrei dovuto definire molte classi differenti in diversi scenari, posso fare qualche "trucco" e renderlo anonimo

Qualcosa di simile:

var k = new {Name = "me", phone = 123}; 
mylist.Add(k); 

Il problema, naturalmente, che non ho un tipo da passare a elenco quando ho definisco

+1

Hai provato la parola chiave dinamica per il tipo di elenco? –

+1

Cosa c'è di sbagliato nel definire la classe? Sembra la soluzione ovvia. Per quanto riguarda la necessità di 'definire molte classi diverse ', forse c'è una struttura di ereditarietà che puoi sfruttare? –

+1

possibile duplicato di [Un elenco generico di classe anonima] (http://stackoverflow.com/questions/612689/a-generic-list-of-anonymous-class) – nawfal

risposta

84

È possibile creare una lista vuota per i tipi anonimi e poi usarlo, che godono di piena intelisence e in fase di compilazione controlli:

var list = Enumerable.Empty<object>() 
      .Select(r => new {A = 0, B = 0}) // prototype of anonymous type 
      .ToList(); 

list.Add(new { A = 4, B = 5 }); // adding actual values 

Console.Write(list[0].A); 
+1

C'è un comodo Select che converte l'oggetto in tipo anonimo, così otteniamo l'elenco dei tipi anonimi, e non l'elenco degli oggetti. Questo esempio viene compilato correttamente. – alex

+0

Hai ragione, +1. – HackedByChinese

+0

In aumento poiché questo è per lo più uguale alla mia risposta, e questa risposta era precedente alla mia. –

17

si potrebbe usare un List<dynamic>.

var myList = new List<dynamic>(); 
myList.Add(new {Tank = new Tank(), AttackingTank = new Tank()}); 

Console.WriteLine("AttackingTank: {0}", myList[0].AttackingTank); 
+0

mentre questo ti permetterà di aggiungere elementi, non consente di assegnare il riferimento a un elenco anonimo. –

2

Che ne dici di ExpandoObject?

dynamic tuple = new ExpandoObject(); 
tuple.WhatEverYouWantTypeOfTank = new Tank(); // Value of any type 

Modifiche:

dynamic tuple = new ExpandoObject(); 
tuple.AttackingTank = new Tank(); 
tuple.TargetTank = new Tank(); 

var mylist = new List<dynamic> { tuple }; 

//access to items 
Console.WriteLine(mylist[0].AttackingTank); 
+0

La tua risposta si limita a creare un tipo in modo dinamico per sostituire 'Tuple', a cui l'OP non ha realmente avuto problemi. Devono sapere come gestire i tipi dinamici in un elenco generico. Rispondete alla vostra risposta e siete a posto (naturalmente, ho già risposto a questa domanda)) – HackedByChinese

+0

@HackedByChinese, beh, aggiungo alcune modifiche ... Inoltre non sono sicuro che il vostro codice funzionerà ... L'inizializzazione di questo oggetto dinamico ('new {Tank = new Tank(), AttackingTank = new Tank()}') non è supportata. – Dmytro

+0

'dynamic' funziona bene con tipi anonimi, purché si osservino le limitazioni generali dei tipi anonimi. Per esempio: se OP dovesse restituire l'elenco risultante come parte di un metodo, ci sarebbero probabilmente problemi a consumare quel risultato da altri assembly (dal momento che i tipi anon sono interni, credo). In tal caso, il tuo 'ExpandoObject' sarebbe necessario, ma a quel punto diventa muto perché OP dovrebbe usare una classe di tuple appropriata per esprimere meglio l'API. – HackedByChinese

6

Ecco un hack:

var myList = Enumerable.Empty<int>() 
    .Select(dummy => new { AttackingTank = default(Tank), TargetTank = default(Tank), }) 
    .ToList(); 

Se Tank è un tipo di classe, è possibile scrivere (Tank)null anziché default(Tank). Puoi anche utilizzare qualche istanza di Tank che hai a portata di mano.


EDIT:

Oppure:

var myList = Enumerable.Repeat(
    new { AttackingTank = default(Tank), TargetTank = default(Tank), }, 
    0).ToList(); 

Se si commette un metodo generico, non sarà necessario utilizzare Enumerable.Empty.Si potrebbe andare in questo modo:

static List<TAnon> GetEmptyListWithAnonType<TAnon>(TAnon dummyParameter) 
{ 
    return new List<TAnon>(); 
} 

Si deve essere chiamato con il TAnon dedurre dal loro utilizzo, naturalmente, come in:

var myList = GetEmptyListWithAnonType(new { AttackingTank = default(Tank), TargetTank = default(Tank), }); 
3

Si potrebbe semplicemente avere un metodo generico che prende un params e lo restituisce:

public static IList<T> ToAnonymousList<T>(params T[] items) 
{ 
    return items; 
} 

Così ora si può dire:

var list=ToAnonymousList 
(
    new{A=1, B=2}, 
    new{A=2, B=2} 
); 
+0

Vorrei restituire la variabile list in un metodo. L'elenco è un IList ma la T è sconosciuta. –

1

O ancora più semplice

 var tupleList = (
      new[] { 
       new { fruit = "Tomato", colour = "red"}, 
       new { fruit = "Tomato", colour = "yellow"}, 
       new { fruit = "Apple", colour = "red"}, 
       new { fruit = "Apple", colour = "green"}, 
       new { fruit = "Medlar", colour = "russet"} 
      }).ToList(); 

che perde la funzione statica.

1

Solo per aggiungere un altro bit utile qui. Io uso la risposta di Alex a volte, ma mi fa un po 'matto cercando di rintracciarlo quando ne ho bisogno, dato che non è ovvio (mi trovo a cercare "new {").

Così ho aggiunto il seguente metodo statico poco (Vorrei poter rendere un metodo di estensione, ma di che tipo?):

public static List<T> CreateEmptyListOf<T>(Func<T> itemCreator) 
{ 
    return Enumerable 
     .Empty<object>() 
     .Select(o => itemCreator()) 
     .ToList(); 
} 

Questa isola la parte che è diverso ogni volta che ho bisogno di questo modello da quelli che sono uguali. Lo chiamo in questo modo:

var emptyList = Ext.CreateEmptyListOf(() => new { Name = default(string), SomeInt = default(int) }); 
+1

Per quanto riguarda la tua domanda: puoi sostituire il corpo del metodo con 'return new List ();'. Ora non è necessario 'Func ' altro ma solo 'T' in modo da poter cambiare la definizione del parametro in' questo T itemSample'. Voilà un metodo di estensione per qualsiasi tipo. – springy76

+0

Sì, funziona anche. Ha un effetto collaterale (probabilmente irrilevante): devi effettivamente creare un'istanza per creare la lista. Entrambi sono un po 'confusi da vedere nel codice. L'uso del tuo metodo sarebbe simile a questo: 'var emptyList = new {Name = default (stringa), SomeInt = default (int)} .CreateEmptyListOf();' che non preferisco; sembra che sto creando qualcosa quando non sono veramente. Ma è davvero solo un'opinione. Davvero il meglio sarebbe se ci fosse un interno o un operatore integrato. – rrreee

+0

Entrambi codice sta creando un oggetto nell'heap: un delegato o un oggetto anonimo stub. In realtà il meglio è usare le classi reali e mantenere l'uso di classi anonime in catene LINQ implicite o esplicite. – springy76

4

Vale la pena notare che, infine, esiste la possibilità di utilizzare tale sintassi. È stato introdotto in C# 7.0

var tanks = new List<(Tank AttackingTank, Tank TargetTank)>(); 

(Tank, Tank) tTank = (new Tank(), new Tank()); 
tanks.Add(tTank); 

var a = tanks[0].AttackingTank; 
var b = tanks[0].TargetTank; 
+1

Mi piace e lo utilizzo nella mia app, ma come si chiama questo tipo di sintassi? Mi piace fare ulteriori ricerche su di esso. –

+0

@ MikeBarlow-BarDev Il nome del blog Microsoft è tipi di tupla e letterali tupla https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/ – Puchacz

Problemi correlati