将来的.NET版本会在C#中支持元组吗?


Answers:


73

我刚刚从MSDN杂志上阅读了这篇文章:Building Tuple

以下是摘录:

即将发布的Microsoft .NET Framework 4.0版本引入了一种称为System.Tuple的新类型。System.Tuple是固定大小的异构类型数据的集合。    

 

像数组一样,元组具有固定的大小,一旦创建就无法更改。与数组不同,元组中的每个元素可以是不同的类型,并且元组能够保证每个元素的强类型化。

 

在System.Collections.Generic命名空间中,已经有一个在Microsoft .NET Framework周围浮动元组的示例:KeyValuePair。虽然可以将KeyValuePair视为与Tuple相同,但由于它们都是两种类型,所以KeyValuePair与Tuple有所不同,因为它唤起了存储的两个值之间的关系(并且有充分的理由,因为它支持Dictionary类)。

此外,元组的大小可以任意设置,而KeyValuePair仅包含两件事:一个键和一个值。


虽然某些语言(例如F#)具有针对元组的特殊语法,但是您可以从任何语言中使用新的通用元组类型。回顾第一个示例,我们可以看到,虽然有用,但是在没有元组语法的语言中,元组可能过于冗长:

class Program {
    static void Main(string[] args) {
        Tuple<string, int> t = new Tuple<string, int>("Hello", 4);
        PrintStringAndInt(t.Item1, t.Item2);
    }
    static void PrintStringAndInt(string s, int i) {
        Console.WriteLine("{0} {1}", s, i);
    }
}

使用C#3.0中的var关键字,我们可以删除元组变量上的类型签名,从而使代码更具可读性。

var t = new Tuple<string, int>("Hello", 4);

我们还向静态Tuple类添加了一些工厂方法,这使使用支持类型推断的语言(如C#)构建元组变得更加容易。

var t = Tuple.Create("Hello", 4);

69
#region tuples

    public class Tuple<T>
    {
        public Tuple(T first)
        {
            First = first;
        }

        public T First { get; set; }
    }

    public class Tuple<T, T2> : Tuple<T>
    {
        public Tuple(T first, T2 second)
            : base(first)
        {
            Second = second;
        }

        public T2 Second { get; set; }
    }

    public class Tuple<T, T2, T3> : Tuple<T, T2>
    {
        public Tuple(T first, T2 second, T3 third)
            : base(first, second)
        {
            Third = third;
        }

        public T3 Third { get; set; }
    }

    public class Tuple<T, T2, T3, T4> : Tuple<T, T2, T3>
    {
        public Tuple(T first, T2 second, T3 third, T4 fourth)
            : base(first, second, third)
        {
            Fourth = fourth;
        }

        public T4 Fourth { get; set; }
    }

    #endregion

并使声明更漂亮:

public static class Tuple
{
    //Allows Tuple.New(1, "2") instead of new Tuple<int, string>(1, "2")
    public static Tuple<T1, T2> New<T1, T2>(T1 t1, T2 t2)
    {
        return new Tuple<T1, T2>(t1, t2);
    }
    //etc...
}

1
虽然问题是MS在.NET 4中提供它,但这是目前管理它的好方法。+1
克里斯·查拉巴鲁克

6
如果您不需要比较两个元组的相等性,则此继承方法很好。我想在实现中实现IEquatable <Tuple <T1,T2 >>等,所以我不能使用继承,因为我不希望Tuple <T1,T2>等于Tuple <T1, T2,T3,T4>。
乔尔·穆勒

3
@Joel,您可以让Equals检查两个参数的动态类型。
RossFabricant

您将如何在代码中使用它?t =新的元组(1,2); t.First和t.Second?
Andriy Drozdyuk,2009年

1
我看到您使用继承-您是否真的希望能够将aTuple<T1,T2,T3>作为传递Tuple<T1,T2>?答案可能不是。
卡伦·罗杰斯

15

Lokad共享库(当然是开源)中有一个适当的(不是很快的)C#Tuple实现,其中包括以下必需功能:

  • 2-5个不可变的元组实现
  • 正确的DebuggerDisplayAttribute
  • 正确的哈希和相等性检查
  • 根据提供的参数(泛型由编译器推断)生成元组的助手,以及用于基于集合的操作的扩展。
  • 经过生产测试。

14

在C#中实现Tuple类或在F#中重用F#类只是故事的一半-这些使您能够相对轻松地创建元组,但实际上不是语法糖,这使得它们在F#等语言中使用起来非常好。

例如,在F#中,您可以使用模式匹配来提取let语句中元组的两个部分,例如

let (a, b) = someTupleFunc

不幸的是,使用来自C#的F#类执行相同的操作会不太优雅:

Tuple<int,int> x = someTupleFunc();
int a = x.get_Item1();
int b = x.get_Item2();

元组表示一种强大的方法,可以从函数调用中返回多个值,而无需使用乱扔类来乱扔代码,也不必诉诸丑陋的ref或out参数。但是,我认为,如果没有一些语法糖来使它们的创建和访问变得更加优雅,它们的使用就会受到限制。


那匿名类型呢?
克里斯·查拉巴鲁克

这样就可以用var替换Tuple <int,int>,但最终还是得到三行代码而不是一行
克里斯·巴拉德

也许这是我们将在C#4.0中看到的东西。语法糖,例如int a,b = someTupleFunc(); 应该在编译器级别完全可行。
亚当·拉瑟克


5

C#7本机支持元组:

var unnamedTuple = ("Peter", 29);
var namedTuple = (Name: "Peter", Age: 29);
(string Name, double Age) typedTuple = ("Peter", 29);

1
.NET 4.7开始,这是开箱即用的,因为.NET的旧版本不包含所需的ValueTuple结构。但是,您可以例如通过诸如ValueTupleBridge之类的NuGet包来提供这些。
tm1

1
MS现在具有用于所需的元组类的官方nuget软件包

3

我的开源.NET Sasa库已有元组很多年了(还有很多其他功能,例如完整的MIME解析)。我已经在生产代码中使用了好几年了。


3

C#非常容易地通过泛型支持简单元组(根据先前的回答),并且通过“含糊的输入”(许多C#语言增强功能之一)来改进类型推断,它们可能非常非常强大。

对于什么是值得,F#支持原生的元组,并且已经打了它,我不知道这(匿名)元组增加多少...你在简洁收获了什么你失去非常的代码清晰快速。

对于单一方法中的代码,有匿名类型;对于超出方法范围的代码,我认为我将坚持使用简单的命名类型。当然,如果将来使用C#可以更轻松地使这些不可变(尽管仍然易于使用),我会很高兴的。


其中一项好处是可用于TryParse之类的东西,您可以在其中编写“有效值= double.TryParse(“ 1.02”)”,并分配多个返回值而无需任何笨拙的out参数。但是总的来说,我同意纯位置数据结构并不是一件好事。
格雷格·比奇

2
同意的-多个返回值是元组的最佳用例,除此之外,代码中元组的用途很少。尽管允许使用元组,但这应该是一种约定而非限制。我认为,如果.NET语言提供了一种将返回的对象“分解”为多个值而不是引入元组数据类型的方法,那将更加整洁。像{ j = ErrorCode, h = ResultObj } = SomeFunction()(where jand hare locals)之类的东西比tuple有用得多。
Walt W 2010年

2

这是我的一组元组,它们是由Python脚本自动生成的,所以我可能有些过头了:

链接到Subversion仓库

您需要一个用户名/密码,他们都是访客

它们基于继承,但是即使碰巧两个前成员的值相同,Tuple<Int32,String>也不会等于Tuple<Int32,String,Boolean>

他们还实现了GetHashCode和ToString等,以及许多小的辅助方法。

用法示例:

Tuple<Int32, String> t1 = new Tuple<Int32, String>(10, "a");
Tuple<Int32, String, Boolean> t2 = new Tuple<Int32, String, Boolean>(10, "a", true);
if (t1.Equals(t2))
    Console.Out.WriteLine(t1 + " == " + t2);
else
    Console.Out.WriteLine(t1 + " != " + t2);

将输出:

10, a != 10, a, True

但这还不是全部。如果它是基于继承的,那么我想您可以编写Tuple<Int32, String> t1 = new Tuple<Int32, String, Boolean>(10, "a", true);等。我不确定是否需要这种情况。
nawfal 2014年

1

如果我还记得我的计算机科学课,那么元组就是数据。

如果要对数据分组,请创建包含属性的类。如果您需要类似KeyValuePair的东西,那就可以了。


3
就像if语句只是带有语句块的命令一样。如果您已经习惯了元组,那么元组就很好了,在某些情况下,类似乎不必要且笨拙……
Daniel O 2009年

除非您需要从一个函数返回5个项目,否则。当然,您可以使自己成为通用类,但是C#7中的语法更加简洁明了。
AustinWBryan '18

0

我会感到惊讶-C#是一种强类型语言,而元组适合于更动态类型的语言。随着时间的推移,C#的动态变化越来越明显,但这是语法上的糖,而不是底层数据类型的真正转变。

如果在一个实例中需要两个值,则KeyValuePair <>是一个不错的替代品,尽管比较笨拙。您还可以使结构或类做相同的事情,并且是可扩展的。


2
“ C#的漂移越来越动态”-不,它的漂移越来越隐式/推断。它仍然是一种完全静态的语言。但是,更好的动态支持(用于消耗DLR类)是将来很可能会增强的语言。
马克·格雷夫

2
您如何解释F#,Haskell或任何其他完全支持它们的强类型和静态类型语言中的元组的存在?
格雷格·比奇

0

为了使它们在哈希表或字典中有用,您可能希望为GetHashCode和Equals提供重载。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.