如何轻松初始化元组列表?

Ste*_*ris 251 c# collections tuples list .net-4.0

我喜欢元组.它们允许您快速将相关信息分组在一起,而无需为其编写结构或类.这在重构非常本地化的代码时非常有用.

然而,初始化它们的列表似乎有点多余.

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

有没有更好的方法?我希望得到一个类似于字典初始化器的解决方案.

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

我们不能使用类似的语法吗?

Ste*_*ris 219

是! 这是可能的.

集合初始值设定项的{}语法适用于任何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 ) );
    }
}
Run Code Online (Sandbox Code Playgroud)

这允许您执行以下操作:

var groceryList = new TupleList<int, string>
{
    { 1, "kiwi" },
    { 5, "apples" },
    { 3, "potatoes" },
    { 1, "tomato" }
};
Run Code Online (Sandbox Code Playgroud)

  • 缺点是必须写课程.和你一样,我喜欢元组,因为我不必编写类或结构. (37认同)
  • @goodeye所以写一次并将其存储在核心库中.,p (7认同)
  • @BillW不太确定这是确切的帖子,... [但我知道Jon Skeet之前已经写过](http://csharpindepth.com/articles/chapter6/iteratorblockimplementation.aspx). (2认同)

tod*_*dmo 214

c#7.0允许你这样做:

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

如果您不需要a List,而只需要一个数组,则可以:

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

如果你不喜欢"Item1"和"Item2",你可以这样做:

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

要么

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

这让你做:tupleList[0].IndextupleList[0].Name

框架4.6.2及以下

您必须System.ValueTuple从Nuget Package Manager 安装.

框架4.7及以上

它内置于框架中.不要安装System.ValueTuple.实际上,删除它从bin目录中删除它.

注意:在现实生活中,我无法在牛,鸡或飞机之间做出选择.我真的会被撕裂.

  • 这可以在 .net core 2.0 上使用吗? (2认同)
  • @АлексаЈевтић,[`System.ValueTuple`](https://www.nuget.org/packages/System.ValueTuple/)支持核心2.0。但是,请先使用Nuget尝试一下,因为不必要的软件包可能会引起问题。因此,是的。如果可能,请使用c#v7或更高版本。 (2认同)

Jef*_*ado 81

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...
}
Run Code Online (Sandbox Code Playgroud)

这将允许您在任何实现的类上执行此操作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 },
};
Run Code Online (Sandbox Code Playgroud)

当然,您不仅限于扩展元组集合,它可以用于您想要特殊语法的任何特定类型的集合.

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)
};
Run Code Online (Sandbox Code Playgroud)

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());
}
Run Code Online (Sandbox Code Playgroud)

这样列表初始化看起来会更好.

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

但是,不要经历所有这些麻烦,切换到ValueTuple专门使用可能更好.

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

  • 这是类型别名可以解决的问题。授予别名本身不能是通用的,这可能是可取的。`使用 TupleList = System.Collections.Generic.List&lt;System.Tuple&lt;int, string&gt;&gt;;` (2认同)

Kev*_*tch 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" )
};
Run Code Online (Sandbox Code Playgroud)

  • 我无法使用原始代码,因此我将其修改为我认为它应该是...这可能会颠覆你对语法"稍微好一些"的看法:) (6认同)
  • 至少使用`Tuple.Create`而你可以推断出类型参数 (5认同)

Asa*_*din 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" )
};
Run Code Online (Sandbox Code Playgroud)