튜플 목록을 쉽게 초기화하는 방법은 무엇입니까?


287

나는 튜플을 좋아한다 . 이를 통해 구조체 나 클래스를 작성하지 않고도 관련 정보를 빠르게 그룹화 할 수 있습니다. 매우 현지화 된 코드를 리팩토링하는 동안 매우 유용합니다.

그러나 목록을 초기화하면 약간 중복되는 것처럼 보입니다.

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

더 좋은 방법이 없습니까? 사전 초기화 프로그램 의 라인을 따라 솔루션을 원합니다 .

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

비슷한 구문을 사용할 수 없습니까?


2
이 경우 왜 튜플 목록 대신 사전을 사용 하지 않습니까?
Ed S.

17
@Ed S .: A Dictionary는 중복 키를 허용하지 않습니다.
Steven Jeuris

2
@EdS .: 매번 하나의 항목이 해시 가능 / 주문 가능하고 고유 한 두 개의 튜플이 아닙니다.

좋은 지적, 중복 키를 보지 못했습니다.
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].IndextupleList[0].Name

프레임 워크 4.6.2 이하

System.ValueTupleNuget Package Manager에서 설치해야합니다 .

프레임 워크 4.7 이상

프레임 워크에 내장되어 있습니다. 마십시오 하지 설치 System.ValueTuple. 사실, 제거 bin 디렉토리에서 삭제.

참고 : 실생활에서는 소, 닭 또는 비행기 중에서 선택할 수 없습니다. 정말 찢어 졌어요


2
이것을 .net core 2.0에서 사용할 수 있습니까?
Алекса Јевтић

2
@ АлексаЈевтић System.ValueTuple는 코어 2.0을 지원합니다. 그러나 불필요한 패키지로 인해 문제가 발생할 수 있으므로 Nuget없이 먼저 사용해보십시오. 그러니 어떤 식 으로든 그래요 가능하면 c # v7 이상을 사용하십시오.
toddmo

1
절대적으로 완벽한 대답! 감사!
Vitox

224

예! 가능합니다 .

컬렉션 이니셜 라이저 의 {} 구문은 올바른 양의 인수를 가진 Add 메서드 가있는 IEnumerable 형식 에서 작동합니다 . 표지 아래에서 작동하는 방식을 귀찮게하지 않으면 간단히 List <T> 에서 확장하고 사용자 정의 Add 메소드를 추가하여 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 이것이 정확한 게시물인지는 확실하지 않지만 ... Jon Skeet이 이전에 그것에 대해 썼다는 것을 알고 있습니다.
Steven Jeuris

1
이 작동 groceryList[0] == groceryList[1]합니까 아니면 비교를 구현해야합니까?
Byyo

이 코드를 유지 관리해야하는 시간을 절약하기 위해 ICollection에서 Add 메서드를 사용하여 Nuget 패키지를 만들었습니다. 패키지 여기를 참조하십시오 : nuget.org/packages/Naos.Recipes.TupleInitializers은 코드 여기를 참조하십시오 : github.com/NaosProject/Naos.Recipes/blob/master/... 이것은 ".Naos에서 솔루션의 CS 파일을 포함합니다 .Recipes "폴더이므로 어셈블리 종속성을 끌어다 놓을 필요가 없습니다.
SFun28

83

C # 6은이를위한 새로운 기능인 extension Add 메소드를 추가합니다. 이것은 항상 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 },
};

물론 튜플 모음을 확장하는 데 제한되지 않으며 특수 구문을 원하는 특정 유형의 모음에 대한 것일 수 있습니다.

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은 언어에 내장 된 튜플에 대한 지원을 추가하지만 다른 유형일 것입니다 ( System.ValueTuple대신). 따라서 가치 튜플에 과부하를 추가하면 사용할 수있는 옵션이 있습니다. 불행하게도, 둘 사이에 정의 된 암시 적 변환은 없습니다.

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

이 코드를 유지 관리해야하는 시간을 절약하기 위해 ICollection에서 Add 메서드를 사용하여 Nuget 패키지를 만들었습니다. 패키지 여기를 참조하십시오 : nuget.org/packages/Naos.Recipes.TupleInitializers은 코드 여기를 참조하십시오 : github.com/NaosProject/Naos.Recipes/blob/master/... 이것은 ".Naos에서 솔루션의 CS 파일을 포함합니다 .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 21'41에

1

Super Duper Old 알고 있지만 C # 7을 사용하는 메소드에 Linq 및 연속 람다 사용에 대한 내 작품을 추가하려고합니다. 클래스에서 재사용 할 때 명명 된 튜플을 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. 나중에 그룹화 방법으로 튜플을 재사용하거나 다른 논리에서 인라인을 구성하는 방법을 사용하십시오.
//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 아니요, 포인트 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
내가 말했듯이 : "그들은 구조체 나 클래스를 작성하지 않고도 관련 정보를 빠르게 그룹화 할 수 있습니다." 이 클래스가 도우미 데이터 구조로 단일 메서드 내에서만 사용되는 경우에는 Tuples를 사용하는 것이 좋습니다. 따라서 네임 스페이스를 불필요한 클래스로 채우지 마십시오.
Steven Jeuris

1
당신은 더 오래된 Tuple 클래스 (struct가 아님)를 생각하고 있습니다. C # 7은 이름을 가질 수있는 구조체 기반 튜플 (새로운 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.