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

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

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

难道没有更好的办法吗?我希望有一个类似Dictionary初始化式的解决方案。

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

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


当前回答

是的!这是可能的。

集合初始化式的{}语法适用于任何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" }
};

其他回答

是的!这是可能的。

集合初始化式的{}语法适用于任何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" }
};

c# 6为此增加了一个新特性:扩展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),
};

我认为有一个技巧比较简单,但之前没有提到过:

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

老问题了,但这是我通常做的事情,让事情更具可读性:

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

您可以通过每次调用构造函数来实现这一点

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