รายการทั่วไปของคลาสที่ไม่ระบุชื่อ


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
โปรดทราบว่าวัตถุทั้งหมดจะต้องพิมพ์เหมือนกันในอาร์เรย์ คุณอาจจำเป็นต้องช่วยนักแสดงโดยเฉพาะอย่างยิ่งสำหรับ nullsnew[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
AaronLS

1
ประเภทที่ไม่ระบุชื่อถูกออกแบบมาเพื่อใช้เป็นที่เก็บข้อมูลชั่วคราวในกรณีส่วนใหญ่คุณจะต้องสร้างมันในคำสั่งเลือก LINQ โดยใช้การเลือก (i => ใหม่ {i.ID, i.Name}); ซึ่งจะส่งคืน IEnumerable ชนิดที่ถูกต้องหากคุณกำหนดประโยคในขณะที่คำสั่งใหม่ลงใน LINQ คำสั่งที่คุณไม่ควรต้องการรายการและถ้าคุณทำคุณก็สามารถเรียก ToList บนมันได้
MikeT

คำตอบ:


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: นั่นจะให้อาร์เรย์ไม่ใช่ List <T>
Jon Skeet

23
@DHornpout: คุณมี "ใช้ System.Linq;" หรือไม่ ที่ด้านบนของไฟล์ของคุณ? ToList เป็นตัวดำเนินการ LINQ
Jon Skeet

5
เข้าใจแล้ว .. ต้องรวมถึง "using System.Linq" ขอบคุณ
DHornpout

2
ดูเหมือนความไม่สอดคล้องกันใน Visual Studio ว่า Intellisense ไม่เป็นประโยชน์ในการเปิดเผยการหายไปรวมถึงแอสเซมบลีที่มีวิธีการส่วนขยายที่อ้างอิง (เช่นเดียวกับประเภทที่อ้างอิง)
LOAS

3
ผู้ชายคนนี้อยู่ทุกหนทุกแห่งค้นหาคำถาม 8 ข้อในวันนี้ 7 ตอบโดยเขา
Kugan Kumar

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
Dutt รหัสของคุณควรทำงานโดยไม่ต้อง. ToList () ที่ส่วนท้าย
DHornpout

3
โอเคเจ๋งตอนนี้เราต้องการตัวอย่างของการแทนที่ {} บรรทัดใหม่ด้วยคำสั่งที่เลือก รายการ var = sourceList.Select (o => ใหม่ {o.ModelId, o.PartNumber, o.Quantity}) ToList ();
topwik

@towpse แก้ปัญหาเกี่ยวกับมันได้หรือไม่
Kiquenet

@ Dutt ตัวอย่างใด ๆ ถ้าฉันใช้วิธีการ (ฟังก์ชั่น) ที่ส่งกลับรายการ <T>?
Kiquenet

ขณะนี้มีวิธีการstring.Joinและสตริงการแก้ไขจึงไม่มีความจำเป็นต้องใช้และforeach Format
realsonic

61

มีหลายวิธีในการทำเช่นนี้ แต่คำตอบบางอย่างที่นี่กำลังสร้างรายการที่มีองค์ประกอบขยะซึ่งคุณต้องล้างรายการ

หากคุณกำลังมองหารายการประเภทที่ว่างเปล่าให้ใช้ตัวเลือก Select to a List of Tuples เพื่อสร้างรายการว่าง องค์ประกอบจะไม่ถูกยกตัวอย่าง

นี่คือหนึ่งซับเพื่อสร้างรายการว่าง:

 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


มันไม่ได้พิมพ์อย่างรุนแรง แต่ในแง่ที่ว่าคุณจะไม่มีคอมไพเลอร์ Intellisense ที่รองรับรายการในรายการ
Joel Coehoorn

31
นี่คือสิ่งที่ฉันกลัวว่าผู้คนจะทำด้วยพลัง
erikkallen

2
ฉันไม่ได้บอกว่ามันเป็นความคิดที่ดีแต่ก็เป็นไปได้ :-) อาจมีความจำเป็นหากการจัดเก็บวัตถุจาก Ruby เช่น
Jeff Moser

2
แต่ในกรณีเหล่านั้นประเภทแหล่งที่มาเป็นแบบไดนามิกหลังจากทั้งหมดมันไม่สมเหตุสมผลที่จะใช้ List <dynamic> สำหรับประเภทที่ไม่ระบุตัวตน
Dykam

1
มีประโยชน์มาก โดยเฉพาะอย่างยิ่งหากต้องกำหนดรายการก่อนที่จะเพิ่มรายการที่ไม่ระบุชื่อ
Karlth

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> เล็กน้อยหลังจากชื่อเมธอด
Martin

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) และทำให้วัตถุแรกของคุณในคำสั่งเลือกเป็น .. วัตถุแรก
Matthew M.

1
คุณไม่จำเป็นต้องเริ่มต้นด้วยรายการที่ว่างเปล่า - ในกรณีที่คุณรู้ว่ารายการแรกคืออะไร (เช่นในตัวอย่าง) แสดงว่าคุณมีความคิดเห็นถูกต้อง หลายครั้งที่ฉันใช้สิ่งนี้ในการแยกวิเคราะห์ไฟล์ / แหล่งข้อมูลกลางและไม่เข้าถึงรายการจริงแรกจนกว่าจะใช้มันในสถานการณ์การฉาย LINQ (และไม่จำเป็นต้องบัญชีสำหรับการข้ามระเบียนแรก)
Rostov

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 ได้ถามไว้
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);

อย่างไรก็ตามคุณจะได้รับข้อผิดพลาด compiletime หากคุณพยายามทำสิ่งนี้ในขอบเขตอื่นแม้ว่าจะใช้งานได้ในขณะทำงาน:

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คุณสมบัติและชื่อชื่อ

ใน. net 4.0 การแก้ปัญหาคือการใช้คำสำคัญistead แบบไดนามิกของวัตถุในรหัสข้างต้น

อีกวิธีคือใช้การสะท้อนเพื่อรับคุณสมบัติ

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

นี่เป็นคำถามเก่า แต่ฉันคิดว่าฉันจะตอบคำถาม 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 ()

ความมหัศจรรย์มาจากการขยายกำหนดเองวิธีการเพิ่มสำหรับ tuples ตามที่อธิบายไว้ใน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 หรือสูงกว่าคุณสามารถใช้tuple typesแทนประเภทที่ไม่ระบุชื่อ

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

คำตอบนี้มีความคิดคล้ายกัน แต่ฉันไม่เห็นจนกว่าฉันจะทำวิธีการเหล่านั้น


0

ลองกับสิ่งนี้:

var result = new List<object>();

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

รายการประเภทที่ไม่ระบุตัวตนอยู่ที่ไหน
Micha Wiedenmann

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.