Загальний список анонімного класу


416

У C # 3.0 ви можете створити анонімний клас із наступним синтаксисом

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

Чи є спосіб додати ці анонімні класи до загального списку?

Приклад:

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

Ще один приклад:

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

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

2
Зауважте, що всі об’єкти повинні бути введені однаково в масиві. Рідко вам може знадобитися допомога з акторським складом, особливо для нулівnew[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
AaronLS

1
анонімні типи призначені для використання у якості тимчасового сховища; у більшості випадків ви створюєте їх у операторі LINQ select за допомогою Select (i => new {i.ID, i.Name}); який би повернув IEnumerable правильного типу, якщо ви перегляньте свою клавішу "while" в LINQ. Де виписка вам ніколи не потрібен, і якщо ви це зробили, то можете просто зателефонувати на нього до
ToList

Відповіді:


427

Ви можете зробити:

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

Існує маса способів зняти цю кішку, але в основному всі вони кудись використовують висновки про тип - це означає, що вам потрібно викликати загальний метод (можливо, як метод розширення). Іншим прикладом може бути:

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

var list = CreateList(o, o1);

Ви отримуєте ідею :)


32
@DHornpout: Це дало б масив, а не Список <T>.
Джон Скіт

23
@DHornpout: Чи є у вас "використання System.Linq;" вгорі вашого файлу? ToList - оператор LINQ.
Джон Скіт

5
Зрозумів .. Необхідно включити "за допомогою System.Linq". Дякую.
DHornpout

2
У Visual Studio здається, що в Visual Studio невідповідність, що intellisense не є більш корисним для розкриття пропущених включень збірок із посилальними методами розширення (те саме, що і посилання на типи).
ЗОБРАЖЕНО

3
ця людина всюди, сьогодні шукала 8 питань, 7 відповідей на нього.
Куган Кумар

109

Ось відповідь.

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
Датт, ваш код повинен працювати без .ToList () наприкінці.
DHornpout

3
гаразд, круто, зараз нам потрібен приклад заміни нових рядків {} на оператор select. var list = sourceList.Select (o => новий {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
topwik

@towpse будь-яке рішення про це?
Кікенет

@Dutt, будь-який зразок, якщо я використовую метод (функцію), який повертає список <T>?
Кікенет

Зараз є методова string.Joinі рядкова інтерполяція, тому не потрібно використовувати foreachі Format.
реальсонічний

61

Існує багато способів зробити це, але деякі відповіді тут створюють список, який містить елементи сміття, що вимагає очищення списку.

Якщо ви шукаєте порожній список загального типу, використовуйте пункт Вибрати проти списку кортежів, щоб зробити порожній список. Жоден елемент не буде створений.

Ось один лайнер для створення порожнього списку:

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

Потім ви можете додати до нього за допомогою свого загального типу:

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

Як альтернативу, ви можете зробити щось на кшталт нижче, щоб створити порожній список (Але я вважаю за краще перший приклад, тому що ви можете використовувати його і для заселеної колекції Tuples):

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

1
Мені це дуже подобається. Спасибі Павло! Завжди хороший день, коли можна використовувати Tuples! xD
Brady Liles

Мені це подобається. Приємно мати якесь конкретне оголошення об’єкта, над яким я збираюся передати.
Morvael

Приємно, що я щойно закінчив писати код, який включав очищення часу мого списку, щоб його переписати
JoshBerke

Дякую за ідею. Пропозиція, ви можете уникнути розподілу фіктивного списку, якщо використовуєтеEnumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe

45

Не зовсім так, але ви можете сказати, List<object>і справи будуть працювати. Однак list[0].Idне вийде.

Це працюватиме під час виконання в C # 4.0, маючи a List<dynamic>, тобто ви не отримаєте IntelliSense.


Це не сильно набрано тексту в тому сенсі, що у вас не буде підтримки для компілятора для інтелігенції для елементів у списку.
Joel Coehoorn

31
Це те, що я боюся, що люди будуть робити динамічно.
erikkallen

2
Я не казав, що це була чудова ідея, але це можливо :-) Можливо, якщо зберігати предмети з Рубі, наприклад, може бути потреба.
Джефф Мозер

2
Але в цих випадках тип джерела динамічний, зрештою, немає сенсу використовувати Список <динамічний> для анонімних типів.
Дікам

1
Дуже корисний. Особливо, якщо список має бути визначений до того, як до нього додаються анонімні елементи.
Карлт

24

я вважаю

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

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

буду працювати.

Ви також можете розглянути можливість написання цього тексту так:

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

Так, це рішення допоможе вирішити ініціалізувати масив Anonymous. Дякую.
DHornpout

1
Просто поставте трохи <T> після назви методу.
Мартін

21

Я зазвичай використовую наступне; головним чином тому, що ви потім "починаєте" зі порожнього списку.

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

Останнім часом я замість цього писав так:

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

Використання методу повтору також дозволить вам зробити:

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

..який дає вам початковий список із уже доданим першим елементом.


2
Вам не потрібно починати з порожнього списку - ви можете виконати діапазон (0,1) і зробити ваш перший об'єкт у операторі select бути першим об'єктом.
Матвій М.

1
Вам не потрібно починати з порожнього списку - у випадку, коли ви знаєте, що таке перший пункт (як у прикладі), тоді ви маєте рацію у своєму коментарі. Хоча я багато разів використовую це для аналізу проміжного файлу / джерела даних і не отримую доступу до першого справжнього елемента, поки не використовую його в сценарії прогнозування LINQ (і, таким чином, не потрібно враховувати пропуск першого запису).
Ростов

19

Це можна зробити у своєму коді.

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

11

В останній версії 4.0 можна використовувати такі динамічні, як нижче

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

10

Я перевірив ІЛ на кілька відповідей. Цей код ефективно забезпечує порожній список:

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

1
Будь-яка причина відхилити мою редакцію? Після відповіді повертається IEnumerable, тоді як моя версія повертається Listсаме в тому, що запитувала ОП.
Necronomicron

Я віддаю перевагу такому підходу або навіть ще ближче до цієї відповіді :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim

8

Ось моя спроба.

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

Я придумав це, коли написав щось подібне для створення списку анонімних для користувацького типу.


8

Можна створити список динамічних.

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

"Динамічний" отримує ініціалізацію першою доданою вартістю.


7

Замість цього:

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

Ви можете це зробити:

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

Однак ви отримаєте помилку компіляції, якщо спробуєте зробити щось подібне в іншому обсязі, хоча це працює під час виконання:

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

Проблема полягає в тому, що під час виконання програми доступні лише члени Object, хоча intellisense відображатиме ідентифікатори властивостей та ім'я .

У .net 4.0 рішенням є використання ключового слова динамічний istaad об'єкта у наведеному вище коді.

Іншим рішенням є використання відображення для отримання властивостей

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

Ось ще один метод створення Списку анонімних типів, який дозволяє починати з порожнього списку, але все ще маєте доступ до IntelliSense.

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

Якщо ви хотіли зберегти перший елемент, просто покладіть одну букву в рядок.

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

Використання рядка як ініціалізатора масиву може працювати, але це дуже погана практика
MikeT

Я б сказав, що більшість наведених відповідей не є особливо «доброю» практикою, але це було певним чином через характер питання. Анонімні типи насправді не були розроблені для роботи таким чином. Мені цікаво, хоча мій метод "гірший", ніж інші? Щось мені не вистачає?
Brackus

Ви маєте рацію, оскільки на питання задається щось таке, що само по собі є поганою практикою, відповідь на хорошу практику не може бути, а тому що ви використовуєте рядок для створення масиву символів, а потім перетворюєте це в масив того, що коли-небудь користувач хоче , які є неспорідненими типами, не кажучи вже про генерування зайвого боксу та розпакування, які неефективні
MikeT

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

5

Це давнє запитання, але я думав, що я поставив би свою відповідь С # 6. Мені часто доводиться встановлювати тестові дані, які легко вводяться в код як список кортежів. За допомогою декількох функцій розширення можна отримати цей приємний, компактний формат, не повторюючи імена на кожному записі.

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

Це дає IEnumerable - якщо ви хочете список, до якого ви можете додати, просто додайте ToList ().

Магія походить від спеціального розширення Додати методи для кортежів, як описано на веб-сторінці 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...

}

Єдине, що мені не подобається, це те, що типи відокремлені від імен, але якщо ви дійсно не хочете складати новий клас, то такий підхід все одно дозволить вам мати читабельні дані.


4

Я дуже здивований, що ніхто не запропонував ініціалізатори колекції. Цей спосіб може додавати об’єкти лише тоді, коли список створений, звідси і назва, однак це виглядає як найприємніший спосіб зробити це. Не потрібно створювати масив, потім перетворите його у список.

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

Ви завжди можете використовувати objectзамість цього, dynamicале намагатися зберегти його справжнім загальним способом, то dynamicмає більше сенсу.


3

Ви можете це зробити так:

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

Мені це здається трохи "хакітним", але це працює - якщо вам справді потрібно мати список і не можете просто використовувати анонімний масив.


3

Для вашого другого прикладу, коли вам потрібно ініціалізувати новий List<T>, одна ідея - створити анонімний список, а потім очистити його.

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

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

Або спосіб розширення повинен бути простішим:

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

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

Або, можливо, навіть коротше,

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

2

Якщо ви використовуєте C # 7 або вище, ви можете використовувати типи кортежу замість анонімних типів.

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

1

Виходячи з цієї відповіді , я придумав два методи, які могли б виконати завдання:

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

Можна використовувати такі методи

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

Ця відповідь має схожу ідею, але я не бачив її лише після того, як я застосував ці методи.



Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.