Общий список анонимного класса


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 (i => new {i.ID, i.Name}); который возвратил бы IEnumerable правильного типа, если вы переопределите предложение while в операторе LINQ.Where, вам никогда не понадобится список, и если вы его сделали, вы можете просто вызвать ToList для него
MikeT

Ответы:


428

Вы могли бы сделать:

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 кажется непоследовательным, что intellisense не более полезен при обнаружении отсутствующих включений сборок с помощью методов расширения, на которые ссылаются, (таких же, как ссылочные типы).
LOAS

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 => new {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
topwik

@ Возникли какие-либо решения по этому поводу?
Kiquenet

@ Датт, какой-нибудь пример, если я использую метод (функцию), который возвращает List <T>?
Kiquenet

Теперь есть метод string.Joinи интерполяция строк, поэтому нет необходимости использовать foreachи Format.
Realsonic

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
Мне очень нравится этот способ. Спасибо, Пол! Это всегда хороший день, когда вы можете использовать кортежи! xD
Брэди Лайлс,

Мне это нравится. Приятно иметь какую-то конкретную декларацию объекта, который я собираюсь передать.
Морваэль

Хорошо, я только что закончил писать код, который включал очистку моего списка времени, чтобы переписать его
JoshBerke

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

45

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

Это будет работать во время выполнения в C # 4.0 при наличии List<dynamic>, то есть вы не получите IntelliSense.


Однако он не является строго типизированным в том смысле, что у вас не будет поддержки intellisense компилятора для элементов в списке.
Джоэл Коухорн

31
Я боюсь, что такие вещи будут делать с динамикой.
erikkallen

2
Я не говорил, что это отличная идея, но это возможно :-) Может возникнуть необходимость, например, хранить объекты из Ruby.
Джефф Мозер

2
Но в этих случаях исходный тип все-таки динамический, нет смысла использовать List <dynamic> для анонимных типов.
Dykam

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

Да, это решение поможет решить инициализацию анонимного массива. Спасибо.
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
Вам не нужно начинать с пустого списка - вы можете выполнить Range (0,1) и сделать свой первый объект в операторе выбора следующим ... первым объектом.
Мэтью М.

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

Я проверил IL на несколько ответов. Этот код эффективно предоставляет пустой список:

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

1
Есть ли причина отклонить мою правку? Следующий ответ возвращает IEnumerable, тогда как моя версия возвращает List, именно то, что спросил OP.
Некрономикрон

Я предпочитаю такой подход, или даже один ближе к этому ответу :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 будет показывать свойства id и name .

В .net 4.0 решением является использование ключевого слова dynamic istead of object в приведенном выше коде.

Другое решение состоит в том, чтобы использовать отражение, чтобы получить свойства

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

Это старый вопрос, но я подумал, что вставлю свой ответ C # 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 ().

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