Một danh sách chung của lớp ẩn danh


416

Trong C # 3.0, bạn có thể tạo lớp ẩn danh với cú pháp sau

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

Có cách nào để thêm các lớp ẩn danh này vào một danh sách chung không?

Thí dụ:

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

Một vi dụ khac:

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

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

2
Lưu ý rằng tất cả các đối tượng phải được gõ giống nhau trong mảng. Hiếm khi bạn có thể cần giúp đỡ với một diễn viên, đặc biệt là đối với nullnew[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
AaronLS

1
các loại ẩn danh được thiết kế để được sử dụng làm bộ nhớ tạm thời, trong hầu hết các trường hợp, bạn sẽ tạo chúng trong câu lệnh chọn LINQ bằng cách sử dụng Chọn (i => new {i.ID, i.Name}); cái này sẽ trả về một số loại IE chính xác nếu bạn xác định lại mệnh đề while của bạn thành một câu lệnh LINQ.Where bạn không bao giờ cần danh sách này và nếu bạn đã có thể thì hãy gọi ToList trên đó
MikeT

Câu trả lời:


427

Bạn có thể làm:

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

Có rất nhiều cách để lột da con mèo này, nhưng về cơ bản, tất cả chúng sẽ sử dụng suy luận kiểu ở đâu đó - điều đó có nghĩa là bạn phải gọi một phương thức chung (có thể là phương pháp mở rộng). Một ví dụ khác có thể là:

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

var list = CreateList(o, o1);

Bạn có ý tưởng :)


32
@DHornpout: Điều đó sẽ cung cấp một mảng, không phải là Danh sách <T>.
Jon Skeet

23
@DHornpout: Bạn có "sử dụng System.Linq;" ở đầu tập tin của bạn? ToList là một toán tử LINQ.
Jon Skeet

5
Có nó .. Cần bao gồm "sử dụng System.Linq". Cảm ơn.
DHornpout

2
Có vẻ như không nhất quán trong Visual Studio, rằng intellisense không hữu ích hơn trong việc phát hiện ra thiếu bao gồm các cụm với các phương thức mở rộng được tham chiếu (giống như các loại được tham chiếu).
LOAS

3
Người đàn ông này ở khắp mọi nơi, tìm kiếm 8 câu hỏi ngày hôm nay, 7 câu trả lời của anh ta.
Kugan Kumar

109

Đây là câu trả lời.

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, mã của bạn sẽ hoạt động mà không có .ToList () ở cuối.
DHornpout

3
được rồi, bây giờ chúng ta cần một ví dụ về việc thay thế các dòng {} mới bằng một câu lệnh chọn. danh sách var = sourceList.Select (o => new {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
topwik

@towpse giải pháp nào về nó?
Kiquenet

@Dutt, bất kỳ mẫu nào nếu tôi sử dụng một phương thức (hàm) trả về Danh sách <T>?
Kiquenet

Bây giờ có string.Joinnội suy phương thức và chuỗi, vì vậy không cần sử dụng foreachFormat.
realsonic

61

Có nhiều cách để làm điều này, nhưng một số câu trả lời ở đây đang tạo một danh sách chứa các thành phần rác, yêu cầu bạn xóa danh sách.

Nếu bạn đang tìm kiếm một danh sách trống của loại chung, hãy sử dụng Chọn đối với Danh sách các Tuples để tạo danh sách trống. Không có yếu tố sẽ được khởi tạo.

Đây là một lớp lót để tạo một danh sách trống:

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

Sau đó, bạn có thể thêm vào nó bằng cách sử dụng loại chung của bạn:

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

Để thay thế, bạn có thể làm một cái gì đó như dưới đây để tạo danh sách trống (Nhưng, tôi thích ví dụ đầu tiên vì bạn cũng có thể sử dụng nó cho một bộ sưu tập Tuples đông dân cư):

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

1
Tôi thích cách này rất nhiều. Cảm ơn Paul! Luôn luôn là một ngày tốt khi bạn có thể sử dụng Tuples! xD
Brady Liles

Tôi thích điều này. Thật tuyệt khi có một số tuyên bố cụ thể về đối tượng mà tôi sẽ thông qua.
Morvael

Rất vui, tôi vừa hoàn thành việc viết mã bao gồm xóa thời gian liệt kê danh sách của mình để viết lại
JoshBerke

Cảm ơn ý kiến. Một gợi ý, bạn có thể tránh phân bổ danh sách giả nếu bạn sử dụngEnumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe

45

Không chính xác, nhưng bạn có thể nói List<object>và mọi thứ sẽ hoạt động. Tuy nhiên, list[0].Idsẽ không làm việc.

Điều này sẽ hoạt động trong thời gian chạy trong C # 4.0 bằng cách có một List<dynamic>, đó là bạn sẽ không nhận được IntelliSense.


Tuy nhiên, nó không được đánh máy mạnh, theo nghĩa là bạn sẽ không có hỗ trợ trình biên dịch hỗ trợ cho các mục trong danh sách.
Joel Coehoorn

31
Đây là loại điều tôi sợ mọi người sẽ làm với năng động.
erikkallen

2
Tôi không nói đó là một ý tưởng tuyệt vời , nhưng điều đó là có thể :-) Có thể có nhu cầu nếu lưu trữ các đối tượng từ Ruby chẳng hạn.
Jeff Moser

2
Nhưng trong những trường hợp đó, loại nguồn là động, thật vô nghĩa khi sử dụng Danh sách <động> cho các loại ẩn danh.
Dykam

1
Rất hữu ích. Đặc biệt là nếu danh sách phải được xác định trước khi các mục ẩn danh được thêm vào nó.
Thứ

24

tôi đoán

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

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

sẽ làm việc.

Bạn cũng có thể xem xét viết nó như thế này:

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

Có giải pháp này sẽ giúp giải quyết khởi tạo mảng Ẩn danh. Cảm ơn.
DHornpout

1
Chỉ cần đặt một chút <T> sau tên phương thức.
Martin

21

Tôi thường sử dụng như sau; chủ yếu là vì sau đó bạn "bắt đầu" với một danh sách trống.

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

Gần đây, tôi đã viết nó như thế này:

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

Sử dụng phương thức lặp lại cũng sẽ cho phép bạn thực hiện:

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

.. trong đó cung cấp cho bạn danh sách ban đầu với mục đầu tiên đã được thêm vào.


2
Bạn không cần bắt đầu với một danh sách trống - bạn có thể thực hiện Phạm vi (0,1) và biến đối tượng đầu tiên của bạn trong câu lệnh chọn thành .. đối tượng đầu tiên.
Matthew M.

1
Bạn không cần phải bắt đầu với một danh sách trống - trong trường hợp bạn biết mục đầu tiên là gì (như trong ví dụ) thì bạn đã đúng trong nhận xét của mình. Nhiều lần tôi sử dụng điều này để phân tích một tệp dữ liệu / nguồn dữ liệu trung gian và không truy cập vào mục thực sự đầu tiên cho đến khi sử dụng nó trong kịch bản chiếu LINQ (và do đó không cần phải bỏ qua bản ghi đầu tiên).
Rostov

19

Bạn có thể làm điều này trong mã của bạn.

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

11

Trong phiên bản 4.0 mới nhất, có thể sử dụng động như dưới đây

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

10

Tôi đã kiểm tra IL trên một số câu trả lời. Mã này cung cấp một danh sách trống:

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

1
Bất kỳ lý do để từ chối chỉnh sửa của tôi? Trả lời câu trả lời IEnumerable, trong khi phiên bản của tôi trả về List, chính xác những gì OP đã hỏi.
Necronomicron

Tôi thích cách tiếp cận này, hoặc thậm chí một cách gần hơn với câu trả lời này :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim

8

Đây là nỗ lực của tôi.

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

Tôi đã nghĩ ra điều này khi tôi viết một cái gì đó tương tự để tạo Danh sách ẩn danh cho loại tùy chỉnh.


8

Bạn có thể tạo một danh sách năng động.

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

"Động" được khởi tạo bởi giá trị gia tăng đầu tiên.


7

Thay vì điều này:

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

Bạn có thể làm điều này:

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

Tuy nhiên, bạn sẽ gặp lỗi compXLime nếu bạn cố gắng làm một cái gì đó như thế này trong phạm vi khác, mặc dù nó hoạt động trong thời gian chạy:

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

Vấn đề là chỉ có các thành viên của Object có sẵn trong thời gian chạy, mặc dù intellisense sẽ hiển thị idtên thuộc tính .

Trong .net 4.0, một giải pháp là sử dụng từ khóa động istead của đối tượng trong đoạn mã trên.

Một giải pháp khác là sử dụng sự phản chiếu để có được các thuộc tính

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

Đây là một phương pháp khác để tạo Danh sách các loại ẩn danh cho phép bạn bắt đầu với một danh sách trống, nhưng vẫn có quyền truy cập vào IntelliSense.

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

Nếu bạn muốn giữ mục đầu tiên, chỉ cần đặt một chữ cái trong chuỗi.

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

Sử dụng một chuỗi làm trình khởi tạo mảng có thể hoạt động nhưng thực tế rất tệ
MikeT

Tôi muốn nói rằng hầu hết các câu trả lời ở trên không phải là thực hành "tốt", nhưng đó là loại được đưa ra do bản chất của câu hỏi. Các loại ẩn danh không thực sự được thiết kế để hoạt động theo cách này. Tôi tò mò mặc dù tại sao phương pháp của tôi "tệ" hơn các phương pháp khác? Cái gì tôi đang thiếu?
Brackus

bạn đúng vì câu hỏi đang yêu cầu một điều gì đó mà bản thân nó không phải là một câu trả lời hay, nhưng vì bạn đang sử dụng một chuỗi để tạo ra một mảng ký tự và sau đó chuyển nó thành một mảng những gì người dùng muốn , đó là những loại không liên quan, không đề cập đến việc tạo ra quyền anh không cần thiết và bỏ hộp không hiệu quả
MikeT

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

5

Đây là một câu hỏi cũ, nhưng tôi nghĩ tôi đã đưa vào câu trả lời C # 6 của mình. Tôi thường phải thiết lập dữ liệu thử nghiệm dễ dàng nhập mã dưới dạng danh sách các bộ dữ liệu. Với một vài chức năng mở rộng, có thể có định dạng nhỏ gọn, đẹp mắt này mà không cần lặp lại tên trên mỗi mục.

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

Điều này cung cấp một IEnumerable - nếu bạn muốn một danh sách mà bạn có thể thêm vào thì chỉ cần thêm ToList ().

Phép thuật đến từ tiện ích mở rộng tùy chỉnh Thêm phương thức cho bộ dữ liệu, như được mô tả tại 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...

}

Điều duy nhất tôi không thích là các loại được tách ra khỏi tên, nhưng nếu bạn thực sự không muốn tạo một lớp mới thì phương pháp này vẫn sẽ cho phép bạn có dữ liệu có thể đọc được.


4

Tôi rất ngạc nhiên không ai đề xuất khởi tạo bộ sưu tập. Cách này chỉ có thể thêm các đối tượng khi danh sách được tạo do đó tên tuy nhiên nó có vẻ như là cách tốt nhất để làm điều đó. Không cần phải tạo một mảng sau đó chuyển đổi nó thành một danh sách.

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

Bạn luôn có thể sử dụng objectthay vì dynamicnhưng cố gắng giữ nó theo cách chung chung thực sự sau đó dynamiccó ý nghĩa hơn.


3

Bạn có thể làm theo cách này:

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

Nó có vẻ hơi "hack" đối với tôi, nhưng nó hoạt động - nếu bạn thực sự cần phải có một danh sách và không thể chỉ sử dụng mảng ẩn danh.


3

Đối với ví dụ thứ hai của bạn, nơi bạn phải khởi tạo một List<T>ý tưởng mới , một ý tưởng là tạo một danh sách ẩn danh, sau đó xóa nó.

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

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

Hoặc như một phương pháp mở rộng, nên dễ dàng hơn:

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

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

Hoặc thậm chí có thể ngắn hơn,

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

2

Nếu bạn đang sử dụng C # 7 trở lên, bạn có thể sử dụng các loại tuple thay vì các loại ẩn danh.

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

1

Xuất phát từ câu trả lời này , tôi đã đưa ra hai phương pháp có thể thực hiện nhiệm vụ:

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

Bạn có thể sử dụng các phương pháp như

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

Câu trả lời này có một ý tưởng tương tự, nhưng tôi đã không thấy nó cho đến khi tôi thực hiện những phương pháp đó.


0

Hãy thử với điều này:

var result = new List<object>();

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

Đâu là danh sách các loại ẩn danh?
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
Tôi thấy không có lớp ẩn danh, ở đây.
Andrew Barber
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.