c# - new - ¿Hay algún truco para crear una lista genérica de tipo anónimo?
new object c# (9)
A veces necesito usar un Tuple, por ejemplo, tengo una lista de tanques y sus tanques objetivo (los persiguen o algo por el estilo):
List<Tuple<Tank,Tank>> mylist = new List<Tuple<Tank,Tank>>();
y luego cuando itero sobre la lista, accedo a ellos
mylist[i].item1 ...
mylist[i].item2 ...
Es muy confuso y siempre me olvido de lo que es item1 y lo que es item2, si pudiera acceder a ellos por:
mylist[i].AttackingTank...
mylist[i].TargetTank...
Sería mucho más claro, ¿hay alguna manera de hacerlo sin definir una clase?
MyTuple
{
public Tank AttackingTank;
public Tank TargetTank;
}
Quiero evitar definir esta clase porque entonces tendría que definir muchas clases diferentes en diferentes escenarios, ¿puedo hacer algún "truco" y hacer esto anónimo?
Algo como :
var k = new {Name = "me", phone = 123};
mylist.Add(k);
El problema, por supuesto, es que no tengo un tipo para pasar a la Lista cuando lo defino
¿Qué hay de ExpandoObject ?
dynamic tuple = new ExpandoObject();
tuple.WhatEverYouWantTypeOfTank = new Tank(); // Value of any type
EDICIONES:
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);
Aquí hay un truco:
var myList = Enumerable.Empty<int>()
.Select(dummy => new { AttackingTank = default(Tank), TargetTank = default(Tank), })
.ToList();
Si Tank
es un tipo de clase, puede escribir (Tank)null
lugar de default(Tank)
. También puedes usar alguna instancia de Tank
que tengas a mano.
EDITAR:
O:
var myList = Enumerable.Repeat(
new { AttackingTank = default(Tank), TargetTank = default(Tank), },
0).ToList();
Si haces un método genérico, no tendrás que usar Enumerable.Empty
. Podría ser así:
static List<TAnon> GetEmptyListWithAnonType<TAnon>(TAnon dummyParameter)
{
return new List<TAnon>();
}
Debe ser invocado con el TAnon
deducido del uso, por supuesto, como en:
var myList = GetEmptyListWithAnonType(new { AttackingTank = default(Tank), TargetTank = default(Tank), });
O incluso más simple
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();
Que pierde la función estática.
Podría usar una List<dynamic>
.
var myList = new List<dynamic>();
myList.Add(new {Tank = new Tank(), AttackingTank = new Tank()});
Console.WriteLine("AttackingTank: {0}", myList[0].AttackingTank);
Puede crear una lista de objetos:
List<object> myList = new List<object>();
A continuación, agregue el tipo anónimo a su lista:
myList.Add(new {Name = "me", phone = 123});
Puede crear una lista vacía para tipos anónimos y luego usarla, disfrutando de intelisence completo y verificaciones en tiempo de compilación:
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);
Solo para agregar un bit más útil aquí. Utilizo la respuesta de Alex en ocasiones, pero me vuelve un poco loco intentar rastrear cuando lo necesito, ya que no es obvio (me encuentro buscando "nuevo {").
Así que agregué el siguiente pequeño método estático (me gustaría poder convertirlo en un método de extensión, pero ¿de qué tipo?):
public static List<T> CreateEmptyListOf<T>(Func<T> itemCreator)
{
return Enumerable
.Empty<object>()
.Select(o => itemCreator())
.ToList();
}
Esto aísla la parte que es diferente cada vez que necesito este patrón de aquellos que son iguales. Lo llamo así:
var emptyList = Ext.CreateEmptyListOf(() => new { Name = default(string), SomeInt = default(int) });
Solo podría tener un método genérico que tome un params
y lo devuelva:
public static IList<T> ToAnonymousList<T>(params T[] items)
{
return items;
}
Entonces ahora puedes decir:
var list=ToAnonymousList
(
new{A=1, B=2},
new{A=2, B=2}
);
Vale la pena señalar que, finalmente, existe la posibilidad de utilizar dicha sintaxis. Se había introducido en 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;