Una lista genérica de clase anónima.

416

En C # 3.0 puede crear una clase anónima con la siguiente sintaxis

var o = new { Id = 1, Name = "Foo" };

¿Hay alguna manera de agregar estas clases anónimas a una lista genérica?

Ejemplo:

var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };

List<var> list = new List<var>();
list.Add(o);
list.Add(o1);

Otro ejemplo:

List<var> list = new List<var>();

while (....)
{
    ....
    list.Add(new {Id = x, Name = y});
    ....
}
DHornpout
fuente
2
Tenga en cuenta que todos los objetos deben escribirse igual en la matriz. Rara vez puede que necesite ayuda con un elenco, especialmente para nulosnew[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
AaronLS
1
los tipos anónimos están diseñados para usarse como almacenamiento temporal, en la mayoría de los casos, los crearía en la instrucción de selección LINQ usando Select (i => new {i.ID, i.Name}); lo que devolvería un IEnumerable del tipo correcto si redefine su cláusula while en un LINQ. Donde la declaración no debería necesitar la lista y si lo hizo, puede llamar a ToList en él
MikeT

Respuestas:

428

Podrías hacerlo:

var list = new[] { o, o1 }.ToList();

Hay muchas formas de desollar a este gato, pero básicamente usarán inferencia de tipos en alguna parte, lo que significa que debe llamar a un método genérico (posiblemente como un método de extensión). Otro ejemplo podría ser:

public static List<T> CreateList<T>(params T[] elements)
{
     return new List<T>(elements);
}

var list = CreateList(o, o1);

Tienes la idea :)

Jon Skeet
fuente
32
@DHornpout: Eso daría una matriz, no una Lista <T>.
Jon Skeet el
23
@DHornpout: ¿Tiene "usando System.Linq;" en la parte superior de su archivo? ToList es un operador LINQ.
Jon Skeet el
55
Lo tengo ... Necesito incluir "usando System.Linq". Gracias.
DHornpout
2
Parece una inconsistencia en Visual Studio, que intellisense no es más útil para descubrir las inclusiones faltantes de ensamblados con métodos de extensión referenciados (igual que los tipos referenciados).
LOAS
3
este hombre está en todas partes, buscó 8 preguntas hoy, 7 contestadas por él.
Kugan Kumar
109

Aquí está la respuesta.

string result = String.Empty;

var list = new[]
{ 
    new { Number = 10, Name = "Smith" },
    new { Number = 10, Name = "John" } 
}.ToList();

foreach (var item in list)
{
    result += String.Format("Name={0}, Number={1}\n", item.Name, item.Number);
}

MessageBox.Show(result);
Dutt
fuente
12
Dutt, tu código debería funcionar sin .ToList () al final.
DHornpout el
3
bien, ahora necesitamos un ejemplo de reemplazo de las nuevas líneas {} con una instrucción select. var list = sourceList.Select (o => new {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
topwik
@towpse alguna solución al respecto?
Kiquenet
@Dutt, ¿alguna muestra si uso un método (función) que devuelve una Lista <T>?
Kiquenet
Ahora hay string.Joininterpolación de métodos y cadenas, por lo que no es necesario usar foreachy Format.
realsonic
61

Hay muchas formas de hacer esto, pero algunas de las respuestas aquí están creando una lista que contiene elementos basura, lo que requiere que borre la lista.

Si está buscando una lista vacía del tipo genérico, use Seleccionar contra una Lista de tuplas para hacer la lista vacía. No se instanciarán elementos.

Aquí está la línea para crear una lista vacía:

 var emptyList = new List<Tuple<int, string>>()
          .Select(t => new { Id = t.Item1, Name = t.Item2 }).ToList();

Luego puede agregarle usando su tipo genérico:

 emptyList.Add(new { Id = 1, Name = "foo" });
 emptyList.Add(new { Id = 2, Name = "bar" });

Como alternativa, puede hacer algo como a continuación para crear la lista vacía (pero, prefiero el primer ejemplo porque también puede usarlo para una colección poblada de tuplas):

 var emptyList = new List<object>()
          .Select(t => new { Id = default(int), Name = default(string) }).ToList();   
Paul Rouleau
fuente
1
Me gusta mucho así. Gracias paul! ¡Siempre es un buen día cuando puedes usar Tuples! xD
Brady Liles
Me gusta esto. Es bueno tener algún tipo de declaración concreta del objeto que voy a pasar.
Morvael
Agradable, acabo de terminar de escribir el código que incluía borrar el tiempo de mi lista para reescribirlo
JoshBerke
Gracias por la idea Una sugerencia, puede evitar la asignación de una lista ficticia si se utilizaEnumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe
45

No exactamente, pero puedes decir List<object>y las cosas funcionarán. Sin embargo, list[0].Idno funcionará.

Esto funcionará en tiempo de ejecución en C # 4.0 al tener un List<dynamic>, es decir, no obtendrá IntelliSense.

Jeff Moser
fuente
Sin embargo, no está fuertemente tipado, en el sentido de que no tendrá soporte intellisense del compilador para los elementos de la lista.
Joel Coehoorn el
31
Este es el tipo de cosas que temo que la gente haga con dinámica.
erikkallen 05 de
2
No dije que fuera una gran idea, pero que era posible :-) Podría ser necesario si se almacenan objetos de Ruby, por ejemplo.
Jeff Moser
2
Pero en esos casos, el tipo de fuente es dinámico después de todo, no tiene sentido usar una Lista <dynamic> para tipos anónimos.
Dykam
1
Muy útil. Especialmente si la lista tiene que definirse antes de agregarle elementos anónimos.
Karlth
24

supongo

List<T> CreateEmptyGenericList<T>(T example) {
    return new List<T>();
}

void something() {
    var o = new { Id = 1, Name = "foo" };
    var emptyListOfAnonymousType = CreateEmptyGenericList(o);
}

trabajará.

También podría considerar escribirlo así:

void something() {
    var String = string.Emtpy;
    var Integer = int.MinValue;
    var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
erikkallen
fuente
Sí, esta solución ayudará a resolver la inicialización de la matriz anónima. Gracias.
DHornpout
1
Simplemente ponga un poco <T> después del nombre del método.
Martin
21

Usualmente uso lo siguiente; principalmente porque luego "comienzas" con una lista que está vacía.

var list = Enumerable.Range(0, 0).Select(e => new { ID = 1, Name = ""}).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
//etc.

Últimamente, lo he estado escribiendo así:

var list = Enumerable.Repeat(new { ID = 1, Name = "" }, 0).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );

Usar el método de repetición también le permitiría hacer:

var myObj = new { ID = 1, Name = "John" };
var list = Enumerable.Repeat(myObj, 1).ToList();
list.Add(new { ID = 2, Name = "Liana" });

..que le da la lista inicial con el primer elemento ya agregado.

Rostov
fuente
2
No necesita comenzar con una lista vacía: puede hacer Range (0,1) y hacer que su primer objeto en la instrucción select sea ... el primer objeto.
Matthew M.
1
No necesita comenzar con una lista vacía: en el caso de que sepa cuál es el primer elemento (como en el ejemplo), entonces tiene razón en su comentario. Aunque muchas veces lo uso para analizar un archivo intermedio / fuente de datos y no accedo al primer elemento verdadero hasta que lo uso en un escenario de proyección LINQ (y, por lo tanto, no es necesario tener en cuenta omitir el primer registro).
Rostov
19

Puedes hacer esto en tu código.

var list = new[] { new { Id = 1, Name = "Foo" } }.ToList();
list.Add(new { Id = 2, Name = "Bar" });
MalachiteBR
fuente
11

En la última versión 4.0, puede usar dinámicas como a continuación

var list = new List<dynamic>();
        list.Add(new {
            Name = "Damith"
    });
        foreach(var item in list){
            Console.WriteLine(item.Name);
        }
    }
Damith Asanka
fuente
10

Revisé el IL en varias respuestas. Este código proporciona eficientemente una lista vacía:

    using System.Linq;
    
    var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
MEC
fuente
1
¿Alguna razón para rechazar mi edición? La siguiente respuesta vuelve IEnumerable, mientras que mi versión regresa List, exactamente lo que OP ha pedido.
Necronomicron
Yo prefiero este enfoque, o incluso uno más cercano a esta respuesta :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim
8

Aquí está mi intento.

List<object> list = new List<object> { new { Id = 10, Name = "Testing1" }, new {Id =2, Name ="Testing2" }}; 

Se me ocurrió esto cuando escribí algo similar para hacer una Lista anónima para un tipo personalizado.

usuario_v
fuente
8

Puede crear una lista de dinámica.

List<dynamic> anons=new List<dynamic>();
foreach (Model model in models)
{
   var anon= new
   {
      Id = model.Id,
      Name=model.Name
   };
   anons.Add(anon);
}

"dinámico" se inicializa con el primer valor agregado.

Nombre de código Jack
fuente
7

En lugar de esto:

var o = new { Id = 1, Name = "Foo" }; 
var o1 = new { Id = 2, Name = "Bar" }; 

List <var> list = new List<var>(); 
list.Add(o); 
list.Add(o1);

Podrías hacer esto:

var o = new { Id = 1, Name = "Foo" }; 
var o1 = new { Id = 2, Name = "Bar" }; 

List<object> list = new List<object>(); 
list.Add(o); 
list.Add(o1);

Sin embargo, obtendrá un error en tiempo de compilación si intenta hacer algo como esto en otro ámbito, aunque funciona en tiempo de ejecución:

private List<object> GetList()
{ 
    List<object> list = new List<object>();
    var o = new { Id = 1, Name = "Foo" }; 
    var o1 = new { Id = 2, Name = "Bar" }; 
    list.Add(o); 
    list.Add(o1);
    return list;
}

private void WriteList()
{
    foreach (var item in GetList()) 
    { 
        Console.WriteLine("Name={0}{1}", item.Name, Environment.NewLine); 
    }
}

El problema es que solo los miembros de Object están disponibles en tiempo de ejecución, aunque intellisense mostrará el ID y el nombre de las propiedades .

En .net 4.0 una solución es usar la palabra clave dinámica en lugar de objeto en el código anterior.

Otra solución es usar la reflexión para obtener las propiedades.

using System;
using System.Collections.Generic;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            var anonymous = p.GetList(new[]{
                new { Id = 1, Name = "Foo" },       
                new { Id = 2, Name = "Bar" }
            });

            p.WriteList(anonymous);
        }

        private List<T> GetList<T>(params T[] elements)
        {
            var a = TypeGenerator(elements);
            return a;
        }

        public static List<T> TypeGenerator<T>(T[] at)
        {
            return new List<T>(at);
        }

        private void WriteList<T>(List<T> elements)
        {
            PropertyInfo[] pi = typeof(T).GetProperties();
            foreach (var el in elements)
            {
                foreach (var p in pi)
                {
                    Console.WriteLine("{0}", p.GetValue(el, null));
                }
            }
            Console.ReadLine();
        }
    }
}
Jakob Flygare
fuente
7

Aquí hay otro método para crear una Lista de tipos anónimos que le permite comenzar con una lista vacía, pero aún así tener acceso a IntelliSense.

var items = "".Select( t => new {Id = 1, Name = "foo"} ).ToList();

Si desea conservar el primer elemento, simplemente ponga una letra en la cadena.

var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Brackus
fuente
El uso de una cadena como inicializador de matriz puede funcionar, pero es una muy mala práctica
MikeT
Yo diría que la mayoría de las respuestas anteriores no son prácticas particularmente "buenas", pero eso fue algo dado debido a la naturaleza de la pregunta. Los tipos anónimos no fueron realmente diseñados para funcionar de esta manera. Sin embargo, tengo curiosidad por qué mi método es "peor" que los demás. Algo que me estoy perdiendo?
Brackus
tiene razón, ya que la pregunta es pedir algo que en sí mismo sea una mala práctica, no puede haber una respuesta de buena práctica, sino porque está usando una cadena para generar una serie de caracteres y luego convertirla en una matriz de lo que el usuario quiera , que son tipos no relacionados, sin mencionar la generación de boxeo superfluo y unboxing que es ineficiente
MikeT
5
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
Morlock
fuente
5

Esta es una vieja pregunta, pero pensé en poner mi respuesta C # 6. A menudo tengo que configurar datos de prueba que se ingresan fácilmente en el código como una lista de tuplas. Con un par de funciones de extensión, es posible tener este formato agradable y compacto, sin repetir los nombres en cada entrada.

var people= new List<Tuple<int, int, string>>() {
    {1, 11, "Adam"},
    {2, 22, "Bill"},
    {3, 33, "Carol"}
}.Select(t => new { Id = t.Item1, Age = t.Item2, Name = t.Item3 });

Esto proporciona un IEnumerable: si desea una lista a la que pueda agregar, simplemente agregue ToList ().

La magia proviene de la extensión personalizada Agregar métodos para tuplas, como se describe en https://stackoverflow.com/a/27455822/4536527 .

public static class TupleListExtensions    {
    public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
            T1 item1, T2 item2)       {
        list.Add(Tuple.Create(item1, item2));
    }

    public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
            T1 item1, T2 item2, T3 item3) {
        list.Add(Tuple.Create(item1, item2, item3));
    }

// and so on...

}

Lo único que no me gusta es que los tipos están separados de los nombres, pero si realmente no desea crear una nueva clase, este enfoque aún le permitirá tener datos legibles.

Peter Davidson
fuente
4

Estoy muy sorprendido de que nadie haya sugerido inicializadores de colección. De esta manera solo se pueden agregar objetos cuando se crea la lista, de ahí el nombre, sin embargo, parece ser la mejor manera de hacerlo. No es necesario crear una matriz y luego convertirla en una lista.

var list = new List<dynamic>() 
{ 
    new { Id = 1, Name = "Foo" }, 
    new { Id = 2, Name = "Bar" } 
};

Siempre se puede usar en objectlugar de dynamicintentar mantenerlo de una manera genérica, pero dynamictiene más sentido.

Tom Dee
fuente
3

Puedes hacerlo de esta manera:

var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };

var array = new[] { o, o1 };
var list = array.ToList();

list.Add(new { Id = 3, Name = "Yeah" });

Me parece un poco "hacky", pero funciona, si realmente necesita tener una lista y no puede usar la matriz anónima.

Jermismo
fuente
3

Para su segundo ejemplo, donde debe inicializar una nueva List<T>, una idea es crear una lista anónima y luego borrarla.

var list = new[] { o, o1 }.ToList();
list.Clear();

//and you can keep adding.
while (....)
{
    ....
    list.Add(new { Id = x, Name = y });
    ....
}

O como método de extensión, debería ser más fácil:

public static List<T> GetEmptyListOfThisType<T>(this T item)
{
    return new List<T>();
}

//so you can call:
var list = new { Id = 0, Name = "" }.GetEmptyListOfThisType();

O probablemente incluso más corto,

var list = new int[0].Select(x => new { Id = 0, Name = "" }).Tolist();
nawfal
fuente
2

Si usa C # 7 o superior, puede usar tipos de tupla en lugar de tipos anónimos.

var myList = new List<(int IntProp, string StrProp)>();
myList.Add((IntProp: 123, StrProp: "XYZ"));
Bassem
fuente
1

Derivando de esta respuesta , se me ocurrieron dos métodos que podrían hacer la tarea:

    /// <summary>
    /// Create a list of the given anonymous class. <paramref name="definition"/> isn't called, it is only used
    /// for the needed type inference. This overload is for when you don't have an instance of the anon class
    /// and don't want to make one to make the list.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="definition"></param>
    /// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
    public static List<T> CreateListOfAnonType<T>(Func<T> definition)
#pragma warning restore RECS0154 // Parameter is never used
    {
        return new List<T>();
    }
    /// <summary>
    /// Create a list of the given anonymous class. <paramref name="definition"/> isn't added to the list, it is
    /// only used for the needed type inference. This overload is for when you do have an instance of the anon
    /// class and don't want the compiler to waste time making a temp class to define the type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="definition"></param>
    /// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
    public static List<T> CreateListOfAnonType<T>(T definition)
#pragma warning restore RECS0154 // Parameter is never used
    {
        return new List<T>();
    }

Puedes usar los métodos como

var emptyList = CreateListOfAnonType(()=>new { Id = default(int), Name = default(string) });
//or
var existingAnonInstance = new { Id = 59, Name = "Joe" };
var otherEmptyList = CreateListOfAnonType(existingAnonInstance);

Esta respuesta tiene una idea similar, pero no la vi hasta que hice esos métodos.

BrainStorm.exe
fuente
0

Prueba con esto:

var result = new List<object>();

foreach (var test in model.ToList()) {
   result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}
Matteo Gariglio
fuente
¿Dónde está la lista de tipo anónimo?
Micha Wiedenmann
-14
static void Main()
{
    List<int> list = new List<int>();
    list.Add(2);
    list.Add(3);
    list.Add(5);
    list.Add(7);
}
Ravi Saini
fuente
55
No veo clases anónimas, aquí.
Andrew Barber