Une liste générique de classe anonyme


416

En C # 3.0, vous pouvez créer une classe anonyme avec la syntaxe suivante

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

Existe-t-il un moyen d'ajouter ces classes anonymes à une liste générique?

Exemple:

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

Un autre exemple:

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

while (....)
{
    ....
    list.Add(new {Id = x, Name = y});
    ....
}

2
Notez que tous les objets doivent être tapés de la même manière dans le tableau. Rarement, vous devrez peut-être aider avec un casting, en particulier pour les new[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
nulls

1
les types anonymes sont conçus pour être utilisés comme stockage temporaire, dans la plupart des cas, vous les créeriez dans l'instruction LINQ select en utilisant Select (i => new {i.ID, i.Name}); qui retournerait un IEnumerable du type correct si vous redéfinissez votre clause while dans une instruction LINQ.Where, vous ne devriez jamais avoir besoin de la liste et si vous l'avez fait, vous pouvez alors simplement appeler ToList dessus
MikeT

Réponses:


428

Vous pourriez faire:

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

Il existe de nombreuses façons de dépouiller ce chat, mais en gros, ils utiliseront tous l'inférence de type quelque part - ce qui signifie que vous devez appeler une méthode générique (éventuellement comme méthode d'extension). Un autre exemple pourrait être:

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

var list = CreateList(o, o1);

Vous avez eu l'idée :)


32
@DHornpout: Cela donnerait un tableau, pas un List <T>.
Jon Skeet

23
@DHornpout: Avez-vous "using System.Linq;" en haut de votre dossier? ToList est un opérateur LINQ.
Jon Skeet

5
Got it .. Besoin d'inclure "using System.Linq". Merci.
DHornpout

2
Il semble qu'une incohérence dans Visual Studio, qu'intellisense ne soit pas plus utile pour découvrir les inclusions manquantes d'assemblys avec des méthodes d'extension référencées (comme les types référencés).
LOAS

3
cet homme est partout, a cherché 8 questions aujourd'hui, 7 réponses par lui.
Kugan Kumar

109

Voici la réponse.

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

12
Dutt, votre code devrait fonctionner sans le .ToList () à la fin.
DHornpout

3
ok cool, maintenant nous avons besoin d'un exemple de remplacement des nouvelles lignes {} par une instruction select. var list = sourceList.Select (o => new {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
topwik

@towpse une solution à ce sujet?
Kiquenet

@Dutt, n'importe quel échantillon si j'utilise une méthode (fonction) qui renvoie un List <T>?
Kiquenet

Il existe maintenant une méthode string.Joinet une interpolation de chaîne, donc pas besoin d'utiliser foreachet Format.
Realsonic

61

Il existe plusieurs façons de le faire, mais certaines des réponses ici créent une liste qui contient des éléments inutiles, ce qui vous oblige à effacer la liste.

Si vous recherchez une liste vide de type générique, utilisez une sélection par rapport à une liste de tuples pour créer la liste vide. Aucun élément ne sera instancié.

Voici le one-liner pour créer une liste vide:

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

Ensuite, vous pouvez y ajouter en utilisant votre type générique:

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

Comme alternative, vous pouvez faire quelque chose comme ci-dessous pour créer la liste vide (Mais, je préfère le premier exemple parce que vous pouvez également l'utiliser pour une collection remplie de Tuples):

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

1
J'aime beaucoup cette façon. Merci Paul! C'est toujours une bonne journée quand vous pouvez utiliser Tuples! xD
Brady Liles

J'aime ça. C'est agréable d'avoir une sorte de déclaration concrète de l'objet que je vais transmettre.
Morvael

Bien, je viens de terminer d'écrire du code qui comprenait de
vider le

Merci pour l'idée. Une suggestion, vous pouvez éviter d'allouer une liste fictive si vous utilisezEnumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe

45

Pas exactement, mais vous pouvez dire List<object>et les choses fonctionneront. Cependant, list[0].Idne fonctionnera pas.

Cela fonctionnera à l'exécution en C # 4.0 en ayant un List<dynamic>, c'est-à-dire que vous n'obtiendrez pas IntelliSense.


Cependant, il n'est pas fortement typé, dans le sens où vous n'aurez pas de support intellisense du compilateur pour les éléments de la liste.
Joel Coehoorn

31
C'est le genre de choses que je crains que les gens ne fassent avec la dynamique.
erikkallen

2
Je n'ai pas dit que c'était une bonne idée, mais que c'était possible :-) Il pourrait y avoir un besoin de stocker des objets de Ruby par exemple.
Jeff Moser

2
Mais dans ces cas, le type source est dynamique après tout, cela n'a aucun sens d'utiliser une liste <dynamique> pour les types anonymes.
Dykam

1
Très utile. Surtout si la liste doit être définie avant d'y ajouter des éléments anonymes.
Karlth

24

j'imagine

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

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

marchera.

Vous pourriez également envisager de l'écrire comme ceci:

void something() {
    var String = string.Emtpy;
    var Integer = int.MinValue;
    var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}

Oui, cette solution aidera à résoudre l'initialisation du tableau anonyme. Merci.
DHornpout

1
Mettez juste un petit <T> après le nom de la méthode.
Martin

21

J'utilise habituellement ce qui suit; principalement parce que vous "commencez" avec une liste vide.

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

Dernièrement, je l'ai écrit comme ceci à la place:

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

L'utilisation de la méthode de répétition vous permettrait également de faire:

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

..qui vous donne la liste initiale avec le premier élément déjà ajouté.


2
Vous n'avez pas besoin de commencer avec une liste vide - vous pouvez faire Range (0,1) et faire de votre premier objet dans l'instruction select le .. premier objet.
Matthew M.

1
Vous n'avez pas besoin de commencer avec une liste vide - dans le cas où vous savez quel est le premier élément (comme dans l'exemple), alors vous avez raison dans votre commentaire. Bien souvent, j'utilise ceci pour analyser un fichier / source de données intermédiaire et n'accède pas au premier élément vrai jusqu'à son utilisation dans un scénario de projection LINQ (et donc je n'ai pas besoin de tenir compte du fait de sauter le premier enregistrement).
Rostov

19

Vous pouvez le faire dans votre code.

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

11

Dans la dernière version 4.0, peut utiliser dynamique comme ci-dessous

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

10

J'ai vérifié l'IL sur plusieurs réponses. Ce code fournit efficacement une liste vide:

    using System.Linq;
    
    var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();

1
Une raison de rejeter ma modification? La réponse suivante renvoie IEnumerable, tandis que ma version revient List, exactement ce que OP a demandé.
Necronomicron

Je préfère cette approche, ou même plus proche de cette réponse :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim

8

Voici ma tentative.

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

Je suis venu avec cela quand j'ai écrit quelque chose de similaire pour faire une liste anonyme pour un type personnalisé.


8

Vous pouvez créer une liste de dynamiques.

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

"dynamique" est initialisé par la première valeur ajoutée.


7

Au lieu de cela:

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

Vous pouvez faire ceci:

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

Cependant, vous obtiendrez une erreur de temps de compilation si vous essayez de faire quelque chose comme ça dans une autre étendue, bien que cela fonctionne à l'exécution:

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

Le problème est que seuls les membres d'Object sont disponibles au moment de l'exécution, bien qu'intellisense affiche l' ID et le nom des propriétés .

Dans .net 4.0, une solution consiste à utiliser le mot clé istead dynamique d' objet dans le code ci-dessus.

Une autre solution consiste à utiliser la réflexion pour obtenir les propriétés

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

7

Voici une autre méthode de création d'une liste de types anonymes qui vous permet de commencer avec une liste vide, tout en ayant accès à IntelliSense.

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

Si vous souhaitez conserver le premier élément, mettez simplement une lettre dans la chaîne.

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

L'utilisation d'une chaîne comme initialiseur de tableau peut fonctionner mais est une très mauvaise pratique
MikeT

Je dirais que la plupart des réponses ci-dessus ne sont pas particulièrement une "bonne" pratique, mais c'était une sorte de donnée en raison de la nature de la question. Les types anonymes n'étaient pas vraiment conçus pour fonctionner de cette façon. Je suis curieux de savoir pourquoi ma méthode est "pire" que les autres? Quelque chose qui me manque?
Brackus

vous avez raison, car la question demande quelque chose qui est en soi une mauvaise pratique, il ne peut pas y avoir de bonne réponse, mais parce que vous utilisez une chaîne pour générer un tableau de caractères, puis en le convertissant en un tableau de ce que l'utilisateur souhaite , qui sont des types non apparentés, sans parler de la génération de boxe et de
déballage

5
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);

5

C'est une vieille question, mais je pensais que je mettrais ma réponse C # 6. Je dois souvent configurer des données de test qui sont facilement saisies dans le code sous forme de liste de tuples. Avec quelques fonctions d'extension, il est possible d'avoir ce joli format compact, sans répéter les noms sur chaque entrée.

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

Cela donne un IEnumerable - si vous voulez une liste que vous pouvez ajouter, ajoutez simplement ToList ().

La magie vient des méthodes d'ajout d'extensions personnalisées pour les tuples, comme décrit sur 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...

}

La seule chose que je n'aime pas, c'est que les types sont séparés des noms, mais si vous ne voulez vraiment pas créer une nouvelle classe, cette approche vous permettra toujours d'avoir des données lisibles.


4

Je suis très surpris que personne n'ait suggéré d'initialisation de collection. Cette façon ne peut ajouter des objets que lorsque la liste est créée, d'où le nom, mais cela semble être la meilleure façon de le faire. Pas besoin de créer un tableau puis de le convertir en liste.

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

Vous pouvez toujours utiliser objectau lieu de, dynamicmais essayer de le garder d'une manière générique véritable a alors dynamicplus de sens.


3

Vous pouvez le faire de cette façon:

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

Cela me semble un peu "hacky", mais cela fonctionne - si vous avez vraiment besoin d'avoir une liste et que vous ne pouvez pas simplement utiliser le tableau anonyme.


3

Pour votre deuxième exemple, où vous devez initialiser un nouveau List<T>, une idée est de créer une liste anonyme, puis de l'effacer.

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

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

Ou comme méthode d'extension, devrait être plus facile:

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

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

Ou probablement encore plus court,

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

2

Si vous utilisez C # 7 ou supérieur, vous pouvez utiliser des types de tuple au lieu de types anonymes.

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

1

Dérivant de cette réponse , j'ai trouvé deux méthodes qui pourraient faire la tâche:

    /// <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>();
    }

Vous pouvez utiliser les méthodes comme

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

Cette réponse a une idée similaire, mais je ne l'ai vue qu'après avoir fait ces méthodes.


0

Essayez avec ceci:

var result = new List<object>();

foreach (var test in model.ToList()) {
   result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}

Où est la liste de type anonyme?
Micha Wiedenmann

-14
static void Main()
{
    List<int> list = new List<int>();
    list.Add(2);
    list.Add(3);
    list.Add(5);
    list.Add(7);
}

5
Je ne vois aucun cours anonyme ici.
Andrew Barber
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.