【问题标题】:How to easily initialize a list of Tuples?如何轻松初始化元组列表?
【发布时间】:2011-12-21 14:15:28
【问题描述】:

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

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

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

没有更好的方法吗?我希望有一个类似于Dictionary initializer 的解决方案。

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

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

【问题讨论】:

  • 在这种情况下,为什么不会使用字典而不是元组列表?
  • @Ed S.:Dictionary 不允许重复键。
  • @EdS.:每次都不是一个二元组,其中一项是可散列/可排序且唯一的。
  • 好点,没有注意到重复的键。

标签: c# list collections .net-4.0 tuples


【解决方案1】:

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 及以下

您必须从 Nuget 包管理器安装 System.ValueTuple

框架 4.7 及更高版本

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

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

【讨论】:

  • 可以在 .net core 2.0 上使用吗?
  • @АлексаЈевтић,System.ValueTuple 支持核心 2.0。但是,请先尝试不使用 Nuget,因为不必要的软件包可能会导致问题。所以无论如何,是的。如果可能,请使用 c# v7 或更高版本。
  • 绝对完美的答案!谢谢!
  • 这是一个相关的microsoft link,关于这个答案中提到的集合初始化器。
【解决方案2】:

是的! This is possible.

集合初始化器的 { } 语法适用于任何 IEnumerable 具有正确数量的参数的 Add 方法的类型。 无需担心它是如何在幕后工作的,这意味着您可以 只需从 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" }
};

【讨论】:

【解决方案3】:

C# 6 为此添加了一个新功能:扩展添加方法。这对于 VB.net 一直是可能的,但现在可以在 C# 中使用。

现在您不必直接将Add() 方法添加到您的类中,您可以将它们实现为扩展方法。当使用Add() 方法扩展任何可枚举类型时,您将能够在集合初始化表达式中使用它。因此,您不必再显式地从列表派生 (as mentioned in another answer),您可以简单地对其进行扩展。

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&lt;&gt; 的类上执行此操作:

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&lt;int, string&gt;.List&lt;Tuple&lt;int, string&gt;&gt; 更具可读性。
  • 这是类型别名可以解决的问题。授予别名本身不能是通用的,这可能更可取。 using TupleList = System.Collections.Generic.List&lt;System.Tuple&lt;int, string&gt;&gt;;
  • 我在 ICollection 上创建了一个带有 Add 方法的 Nuget 包,以节省其他人必须维护此代码的时间。请参阅此处获取包:nuget.org/packages/Naos.Recipes.TupleInitializers 请参阅此处获取代码:github.com/NaosProject/Naos.Recipes/blob/master/… 这将在您的解决方案中的“.Naos.Recipes”文件夹下包含一个 cs 文件,因此您不必拖动程序集依赖项跨度>
【解决方案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" )
};

【讨论】:

  • 我无法让原始代码工作,所以我将它修改为我认为应该的......这可能会扭转你对语法是否“稍微好一点”之后的看法全部:)
  • 至少使用Tuple.Create,你可以推断类型参数
  • 这就是我要找的。谢谢@onedaywhen
【解决方案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" )
};

【讨论】:

  • 也适用于旧版本的 .NET!
【解决方案6】:

Super Duper Old 我知道,但我会在使用 C# 7 的方法上添加关于使用 Linq 和延续 lambda 的文章。当在类中重用时,我尝试使用命名元组作为 DTO 和匿名投影的替代品。是的,对于模拟和测试,您仍然需要类,但是内联做事并在类中传递很高兴拥有这个更新的选项恕我直言。您可以从

实例化它们
  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());

【讨论】:

  • 你让我觉得自己老了。 :) 我可能在这里遗漏了一些东西,但是初始化'holds'一点也不简洁。这正是问题的重点。
  • @Steven Jeuris 不,你说得对,我为第 1 点复制并粘贴了错误的代码。在点击“提交”之前需要慢一点。
【解决方案7】:

我认为一种更简单的技术,这里之前没有提到过:

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 是否一样徒劳无功。我个人更喜欢显式类型(当它在构造函数中不重复时)。
【解决方案8】:
    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>

【讨论】:

  • 如何使用元组获取值/名称语法?
  • 编译器告诉匿名类型不能隐式转换为元组
猜你喜欢
  • 2023-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
  • 1970-01-01
  • 2016-08-19
相关资源
最近更新 更多