Как легко инициализировать список кортежей?

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

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

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

Нельзя ли использовать подобный синтаксис?

Ответ 1

С# 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

Рамки 4.6.2 и ниже

Вы должны установить System.ValueTuple из диспетчера пакетов Nuget.

Рамка 4.7 и выше

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

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

Ответ 2

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

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

Ответ 3

С# 6 добавляет новую функцию только для этого: расширение Добавить методы. Это всегда было возможно для VB.net, но теперь доступно на С#.

Теперь вам не нужно добавлять методы 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)
};

С# 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),
};

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

Один метод, который я считаю немного легче, и об этом не упоминалось ранее:

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

Ответ 7

Super Duper Old Я знаю, но я бы добавил свою статью об использовании Linq и продолжения lambdas в методах с использованием С# 7. Я пытаюсь использовать именованные кортежи в качестве замен для 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. Повторное использование кортежей позже с помощью методов группировки или использование метода для их построения inline в другой логике:
//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());

Ответ 8

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

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

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

Ответ 9

    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>