Как легко инициализировать список кортежей? - PullRequest
228 голосов
/ 04 ноября 2011

Я люблю кортежи .Они позволяют вам быстро сгруппировать релевантную информацию без необходимости писать структуру или класс для нее.Это очень полезно при рефакторинге очень локализованного кода.

Инициализация списка из них, однако, выглядит несколько избыточной.

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

Разве мы не можем использовать подобный синтаксис?

Ответы [ 9 ]

214 голосов
/ 04 ноября 2011

Да! Это возможно .

Синтаксис {} инициализатора коллекции работает на любом типе IEnumerable , который имеет Добавьте метод с правильным количеством аргументов.Не беспокоясь о том, как это работает под одеялом, это означает, что вы можете просто перейти от List , добавить собственный метод 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" }
};
170 голосов
/ 25 марта 2017

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.

Framework4.7 и выше

Он встроен в каркас. не установить System.ValueTuple.Фактически, удалите его и удалите его из каталога мусорного ведра.

примечание: в реальной жизни я не смог бы выбирать между коровой, курицей или самолетом.Я был бы действительно разорван.

79 голосов
/ 13 декабря 2014

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

Конечно, вы не ограничены расширением коллекций кортежей, это может быть для коллекций любого определенного типа, для которого вы хотите специальный синтаксис.

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),
};
41 голосов
/ 04 ноября 2011

Вы можете сделать это, вызывая конструктор каждый раз, когда чуть лучше

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" )
};
28 голосов
/ 31 мая 2015

Старый вопрос, но это то, что я обычно делаю, чтобы сделать вещи немного более читабельными:

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" )
};
1 голос
/ 06 декабря 2018

Super Duper Old Я знаю, но я бы добавил свою статью об использовании Linq и лямбда-выражений продолжения для методов с использованием C # 7. Я пытаюсь использовать именованные кортежи в качестве замены DTO и анонимных проекций при повторном использовании в классе.Да, для насмешек и тестирования вам все еще нужны классы, но делать что-то встроенное и проходить в классе хорошо, если у вас есть этот новый вариант ИМХО.Вы можете создать их экземпляр из

  1. Прямое создание экземпляров
var items = new List<(int Id, string Name)> { (1, "Me"), (2, "You")};
Выключен из существующей коллекции, и теперь вы можете возвращать правильно набранные кортежи, аналогично тому, как это делали анонимные проекции.
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));
Повторно используйте кортежи позже с методами группировки или используйте метод для их встроенного построения в другой логике:
//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());
0 голосов
/ 23 января 2018

Одна техника, я думаю, немного проще, и она не упоминалась здесь ранее:

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")
};
0 голосов
/ 03 марта 2017

Почему нравятся кортежи? Это как анонимные типы: без имен. Не могу понять структуру данных.

Мне нравятся классические занятия

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" }
};
0 голосов
/ 01 октября 2015
    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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...