วิธีการเริ่มต้นรายการ Tuples ได้อย่างง่ายดาย?


287

ฉันรักtuples ช่วยให้คุณสามารถจัดกลุ่มข้อมูลที่เกี่ยวข้องเข้าด้วยกันได้อย่างรวดเร็วโดยไม่ต้องเขียน struct หรือคลาสของมัน สิ่งนี้มีประโยชน์มากในขณะที่ refactoring รหัสที่แปลเป็นภาษาท้องถิ่นมาก

การเริ่มต้นรายการของพวกเขา แต่ดูเหมือนว่าซ้ำซ้อน

var tupleList = new List<Tuple<int, string>>
{
    Tuple.Create( 1, "cow" ),
    Tuple.Create( 5, "chickens" ),
    Tuple.Create( 1, "airplane" )
};

ไม่มีวิธีที่ดีกว่านี้หรือ ฉันจะรักวิธีการแก้ปัญหาตามสายของที่initializer พจนานุกรม

Dictionary<int, string> students = new Dictionary<int, string>()
{
    { 111, "bleh" },
    { 112, "bloeh" },
    { 113, "blah" }
};

เราไม่สามารถใช้ไวยากรณ์ที่คล้ายกันได้หรือไม่


2
ในกรณีนี้ทำไมคุณไม่ใช้พจนานุกรมแทนรายการ Tuples
Ed S.

17
@Ed S .: A Dictionaryไม่อนุญาตให้ใช้คีย์ที่ซ้ำกัน
Steven Jeuris

2
@ แก้ไข: ทุกครั้งที่ไม่ใช่สองทูเปิลโดยที่หนึ่งไอเท็มนั้นสามารถแฮช / สั่งได้และไม่ซ้ำกัน

จุดดีไม่ได้สังเกตเห็นรหัสซ้ำกัน
Ed S.

คำตอบ:


280

c # 7.0 ให้คุณทำสิ่งนี้:

  var tupleList = new List<(int, string)>
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

หากคุณไม่ต้องการListแต่เพียงอาร์เรย์คุณสามารถทำสิ่งต่อไปนี้

  var tupleList = new(int, string)[]
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

และถ้าคุณไม่ชอบ "Item1" และ "Item2" คุณสามารถทำได้:

  var tupleList = new List<(int Index, string Name)>
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

หรือสำหรับอาร์เรย์:

  var tupleList = new (int Index, string Name)[]
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

ซึ่งช่วยให้คุณทำ: tupleList[0].IndexและtupleList[0].Name

Framework 4.6.2 และต่ำกว่า

คุณต้องติดตั้งSystem.ValueTupleจาก Nuget Package Manager

กรอบงาน 4.7 ขึ้นไป

มันถูกสร้างขึ้นในกรอบ ไม่ได้System.ValueTupleติดตั้ง ในความเป็นจริงให้ลบออกและลบออกจากไดเรกทอรีช่องเก็บ

หมายเหตุ: ในชีวิตจริงฉันไม่สามารถเลือกได้ระหว่างวัวไก่หรือเครื่องบิน ฉันจะถูกฉีกขาดจริงๆ


2
สามารถใช้กับ. net core 2.0 ได้หรือไม่
АлексаЈевтић

2
@ АлексаЈевтић System.ValueTupleรองรับ core 2.0 แต่ลองใช้โดยไม่มี Nuget ก่อนเนื่องจากแพ็คเกจที่ไม่จำเป็นอาจทำให้เกิดปัญหาได้ ไม่ทางใดก็ทางหนึ่งใช่ ใช้ c # v7 หรือสูงกว่าถ้าเป็นไปได้
toddmo

1
คำตอบที่สมบูรณ์แบบแน่นอน! ขอบคุณ!
Vitox

224

ใช่ นี้เป็นไปได้

{}ไวยากรณ์ของคอลเลกชันการเริ่มต้นทำงานในใด ๆIEnumerable ประเภทซึ่งมีการเพิ่มวิธีการที่มีจำนวนที่ถูกต้องของการขัดแย้ง นั่นหมายความว่าคุณสามารถขยายจากรายการ <T>เพิ่มวิธีการเพิ่มที่กำหนดเองเพื่อเริ่มต้นTของคุณ และทำเสร็จแล้ว!

public class TupleList<T1, T2> : List<Tuple<T1, T2>>
{
    public void Add( T1 item, T2 item2 )
    {
        Add( new Tuple<T1, T2>( item, item2 ) );
    }
}

สิ่งนี้อนุญาตให้คุณทำสิ่งต่อไปนี้:

var groceryList = new TupleList<int, string>
{
    { 1, "kiwi" },
    { 5, "apples" },
    { 3, "potatoes" },
    { 1, "tomato" }
};

39
ข้อเสียคือต้องเขียนชั้นเรียน เช่นเดียวกับคุณฉันชอบสิ่งอันดับเพราะฉันไม่ต้องเขียนชั้นเรียนหรือวางโครงสร้าง
goodeye

2
@BillW ไม่บางเกินไปนี้เป็นโพสต์ที่แน่นอน ... แต่ฉันรู้ว่าจอนสกีตได้เขียนเกี่ยวกับมันก่อน
Steven Jeuris

1
ใช้งานได้groceryList[0] == groceryList[1]หรือต้องทำการเปรียบเทียบ
Byyo

ฉันสร้างแพคเกจ Nuget พร้อมเพิ่มวิธีใน ICollection เพื่อช่วยผู้อื่นในการรักษารหัสนี้ ดูที่นี่สำหรับแพ็คเกจ: nuget.org/packages/Naos.Recipes.TupleInitializersดูที่นี่สำหรับรหัส: github.com/NaosProject/Naos.Recipes/blob/master/… ซึ่งจะรวมไฟล์ cs ในโซลูชันของคุณภายใต้ ".Naos .Recipes "โฟลเดอร์ดังนั้นคุณไม่ต้องลากไปรอบ ๆ การพึ่งพาการชุมนุม
SFun28

83

C # 6 เพิ่มคุณสมบัติใหม่เพียงแค่นี้: ส่วนขยายเพิ่มวิธีการ สิ่งนี้เป็นไปได้สำหรับ VB.net แต่ตอนนี้มีให้บริการใน C # แล้ว

ตอนนี้คุณไม่ต้องเพิ่มAdd()วิธีการในชั้นเรียนของคุณโดยตรงคุณสามารถใช้พวกเขาเป็นวิธีการขยาย เมื่อขยายประเภทใด ๆ ที่นับได้ด้วยAdd()วิธีการคุณจะสามารถใช้มันในการรวบรวมการแสดงออกเริ่มต้น ดังนั้นคุณไม่จำเป็นต้องได้รับจากรายการอย่างชัดเจนอีกต่อไป ( ดังที่กล่าวไว้ในคำตอบอื่น ) คุณสามารถขยายได้

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...
}

นี้จะช่วยให้คุณสามารถทำเช่นนี้ในระดับใด ๆ ที่ดำเนินการIList<>:

var numbers = new List<Tuple<int, string>>
{
    { 1, "one" },
    { 2, "two" },
    { 3, "three" },
    { 4, "four" },
    { 5, "five" },
};
var points = new ObservableCollection<Tuple<double, double, double>>
{
    { 0, 0, 0 },
    { 1, 2, 3 },
    { -4, -2, 42 },
};

แน่นอนว่าคุณไม่ได้ จำกัด การขยายคอลเลกชันของ tuples มันอาจเป็นคอลเลกชันประเภทเฉพาะที่คุณต้องการไวยากรณ์พิเศษ

public static class BigIntegerListExtensions
{
    public static void Add(this IList<BigInteger> list,
        params byte[] value)
    {
        list.Add(new BigInteger(value));
    }

    public static void Add(this IList<BigInteger> list,
        string value)
    {
        list.Add(BigInteger.Parse(value));
    }
}

var bigNumbers = new List<BigInteger>
{
    new BigInteger(1), // constructor BigInteger(int)
    2222222222L,       // implicit operator BigInteger(long)
    3333333333UL,      // implicit operator BigInteger(ulong)
    { 4, 4, 4, 4, 4, 4, 4, 4 },               // extension Add(byte[])
    "55555555555555555555555555555555555555", // extension Add(string)
};

C # 7 จะเพิ่มการสนับสนุนสำหรับ tuples ที่สร้างขึ้นในภาษาแม้ว่าจะเป็นประเภทอื่น ( System.ValueTupleแทน) ดังนั้นจะเป็นการดีที่จะเพิ่มการโอเวอร์โหลดสำหรับค่า tuples เพื่อให้คุณมีตัวเลือกในการใช้เช่นกัน น่าเสียดายที่ไม่มีการแปลงที่กำหนดโดยนัยระหว่างทั้งสอง

public static class ValueTupleListExtensions
{
    public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
        ValueTuple<T1, T2> item) => list.Add(item.ToTuple());
}

วิธีการเริ่มต้นรายการนี้จะดูดีกว่า

var points = new List<Tuple<int, int, int>>
{
    (0, 0, 0),
    (1, 2, 3),
    (-1, 12, -73),
};

แต่แทนที่จะต้องเจอกับปัญหาทั้งหมดนี้มันอาจจะเป็นการดีกว่าถ้าคุณเปลี่ยนมาใช้โดยValueTupleเฉพาะ

var points = new List<(int, int, int)>
{
    (0, 0, 0),
    (1, 2, 3),
    (-1, 12, -73),
};

ในที่สุดฉันก็ดูดีในขณะนี้การปรับปรุงห้องสมุดของฉันเป็น C # 6.0 แม้ว่ามันจะดูดีได้อย่างรวดเร็ว, ฉันชอบของฉันวิธีการแก้ปัญหาที่โพสต์ก่อนหน้านี้ในช่วงนี้เพราะผมหาที่จะอ่านได้มากกว่าTupleList<int, string>. List<Tuple<int, string>>
Steven Jeuris

1
นั่นเป็นสิ่งที่นามแฝงประเภทสามารถแก้ไขได้ ได้รับนามแฝงเองไม่สามารถทั่วไปซึ่งอาจจะดีกว่า using TupleList = System.Collections.Generic.List<System.Tuple<int, string>>;
Jeff Mercado

ฉันสร้างแพคเกจ Nuget พร้อมเพิ่มวิธีใน ICollection เพื่อช่วยผู้อื่นในการรักษารหัสนี้ ดูที่นี่สำหรับแพ็คเกจ: nuget.org/packages/Naos.Recipes.TupleInitializersดูที่นี่สำหรับรหัส: github.com/NaosProject/Naos.Recipes/blob/master/…ซึ่งจะรวมไฟล์ cs ในโซลูชันของคุณภายใต้ ".Naos .Recipes "โฟลเดอร์ดังนั้นคุณไม่ต้องลากไปรอบ ๆ การพึ่งพาการชุมนุม
SFun28

42

คุณสามารถทำได้โดยการเรียกนวกรรมิกในแต่ละครั้งด้วยจะดีกว่าเล็กน้อย

var tupleList = new List<Tuple<int, string>>
{
    new Tuple<int, string>(1, "cow" ),
    new Tuple<int, string>( 5, "chickens" ),
    new Tuple<int, string>( 1, "airplane" )
};

6
ฉันไม่สามารถใช้รหัสดั้งเดิมในการทำงานได้ดังนั้นฉันจึงแก้ไขให้เป็นสิ่งที่ฉันคิดว่าควรจะเป็น ... ซึ่งอาจย้อนความเห็นของคุณว่าไวยากรณ์ "ดีขึ้นเล็กน้อย" หลังจากทั้งหมด :)
onedaywhen

5
ใช้อย่างน้อยTuple.Createแทนและคุณสามารถอนุมานชนิดอาร์กิวเมนต์ได้
Dave Cousineau

28

คำถามเก่า แต่นี่คือสิ่งที่ฉันมักจะทำเพื่อให้อ่านง่ายขึ้น:

Func<int, string, Tuple<int, string>> tc = Tuple.Create;

var tupleList = new List<Tuple<int, string>>
{
    tc( 1, "cow" ),
    tc( 5, "chickens" ),
    tc( 1, "airplane" )
};

ทำงานบน. NET เวอร์ชั่นเก่าด้วยเช่นกัน!
Ed Bayiates

1

Super Duper Old ฉันรู้ แต่ฉันจะเพิ่มส่วนของฉันในการใช้ Linq และ lambdas ที่ต่อเนื่องกับวิธีการใช้ C # 7 ฉันพยายามใช้ tuples ที่มีชื่อเป็นสิ่งทดแทนสำหรับ DTO และการคาดการณ์แบบไม่ระบุชื่อเมื่อนำมาใช้ใหม่ในชั้นเรียน ใช่สำหรับการเยาะเย้ยและทดสอบคุณยังต้องการคลาส แต่การทำสิ่งต่าง ๆ แบบอินไลน์และการส่งไปในชั้นเรียนเป็นการดีที่มี IMHO ตัวเลือกใหม่นี้ คุณสามารถยกตัวอย่างพวกเขาจาก

  1. การเริ่มต้นโดยตรง
var items = new List<(int Id, string Name)> { (1, "Me"), (2, "You")};
  1. ไม่อยู่ในคอลเล็กชันที่มีอยู่และตอนนี้คุณสามารถคืนค่าทูเปิลที่พิมพ์ได้ดีคล้ายกับวิธีฉายภาพแบบไม่ระบุชื่อที่ใช้
public class Hold
{
    public int Id { get; set; }
    public string Name { get; set; }
}

//In some method or main console app:
var holds = new List<Hold> { new Hold { Id = 1, Name = "Me" }, new Hold { Id = 2, Name = "You" } };
var anonymousProjections = holds.Select(x => new { SomeNewId = x.Id, SomeNewName = x.Name });
var namedTuples = holds.Select(x => (TupleId: x.Id, TupleName: x.Name));
  1. ใช้ tuples ในภายหลังด้วยวิธีการจัดกลุ่มหรือใช้วิธีการสร้างแบบอินไลน์ในตรรกะอื่น ๆ :
//Assuming holder class above making 'holds' object
public (int Id, string Name) ReturnNamedTuple(int id, string name) => (id, name);
public static List<(int Id, string Name)> ReturnNamedTuplesFromHolder(List<Hold> holds) => holds.Select(x => (x.Id, x.Name)).ToList();
public static void DoSomethingWithNamedTuplesInput(List<(int id, string name)> inputs) => inputs.ForEach(x => Console.WriteLine($"Doing work with {x.id} for {x.name}"));

var namedTuples2 = holds.Select(x => ReturnNamedTuple(x.Id, x.Name));
var namedTuples3 = ReturnNamedTuplesFromHolder(holds);
DoSomethingWithNamedTuplesInput(namedTuples.ToList());

คุณทำให้ฉันรู้สึกแก่เฒ่า :) ฉันอาจจะพลาดอะไรบางอย่างที่นี่ แต่การเริ่มต้น 'การระงับ' นั้นไม่รัดกุมเลย นี่คือจุดที่แน่นอนของคำถาม
Steven Jeuris

@Steven Jeuris ไม่ถูกต้องฉันคัดลอกและวางโค้ดผิดสำหรับ Point 1 ต้องช้าลงเล็กน้อยก่อนที่ฉันจะกด 'ส่ง'
djangojazz

0

ทำไมถึงชอบสิ่งอันดับ มันเหมือนประเภทที่ไม่ระบุชื่อ: ไม่มีชื่อ ไม่สามารถเข้าใจโครงสร้างของข้อมูล

ฉันชอบคลาสสิก

class FoodItem
{
     public int Position { get; set; }
     public string Name { get; set; }
}

List<FoodItem> list = new List<FoodItem>
{
     new FoodItem { Position = 1, Name = "apple" },
     new FoodItem { Position = 2, Name = "kiwi" }
};

3
ดังที่ฉันได้กล่าวไว้: "พวกมันอนุญาตให้คุณจัดกลุ่มข้อมูลที่เกี่ยวข้องได้อย่างรวดเร็วด้วยกันโดยไม่ต้องเขียน struct หรือคลาสเลย" ในกรณีที่คลาสนี้จะถูกใช้ภายในวิธีการเดียวเป็นโครงสร้างข้อมูลผู้ช่วยเหลือฉันชอบใช้ Tuples ดังนั้นไม่ควรใช้ namespace กับคลาสที่ไม่จำเป็น
Steven Jeuris

1
คุณกำลังคิดถึงคลาส Tuple รุ่นเก่า (ไม่ใช่ struct) C # 7 ได้เพิ่ม tuples ที่อิงตามโครงสร้าง (ได้รับการสนับสนุนโดย ValueTuple ชนิดใหม่) ที่สามารถมีชื่อเพื่อให้คุณสามารถเข้าใจโครงสร้างของข้อมูล
Steve Niles

0

เทคนิคหนึ่งที่ฉันคิดว่าง่ายกว่าเล็กน้อยและไม่เคยพูดถึงมาก่อน:

var asdf = new [] { 
    (Age: 1, Name: "cow"), 
    (Age: 2, Name: "bird")
}.ToList();

ฉันคิดว่ามันค่อนข้างสะอาดกว่า:

var asdf = new List<Tuple<int, string>> { 
    (Age: 1, Name: "cow"), 
    (Age: 2, Name: "bird")
};

ซึ่งจะทำให้คุณได้ลิ้มลองมากขึ้นไม่ว่าคุณจะชอบพูดถึงประเภทอย่างชัดเจนหรือไม่ก็ตามในสิ่งเดียวกันกับที่จะใช้varหรือไม่ ฉันชอบพิมพ์อย่างชัดเจนโดยส่วนตัว (เมื่อไม่ซ้ำในเช่นตัวสร้าง)
Steven Jeuris

-4
    var colors = new[]
    {
        new { value = Color.White, name = "White" },
        new { value = Color.Silver, name = "Silver" },
        new { value = Color.Gray, name = "Gray" },
        new { value = Color.Black, name = "Black" },
        new { value = Color.Red, name = "Red" },
        new { value = Color.Maroon, name = "Maroon" },
        new { value = Color.Yellow, name = "Yellow" },
        new { value = Color.Olive, name = "Olive" },
        new { value = Color.Lime, name = "Lime" },
        new { value = Color.Green, name = "Green" },
        new { value = Color.Aqua, name = "Aqua" },
        new { value = Color.Teal, name = "Teal" },
        new { value = Color.Blue, name = "Blue" },
        new { value = Color.Navy, name = "Navy" },
        new { value = Color.Pink, name = "Pink" },
        new { value = Color.Fuchsia, name = "Fuchsia" },
        new { value = Color.Purple, name = "Purple" }
    };
    foreach (var color in colors)
    {
        stackLayout.Children.Add(
            new Label
            {
                Text = color.name,
                TextColor = color.value,
            });
        FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
    }

this is a Tuple<Color, string>

คุณจะได้รับค่า / ชื่อไวยากรณ์ด้วย Tuple ได้อย่างไร
Andreas Reiff

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