.NET4.0中非常好用的一个东西Tuple

       最近发现了.NET4.0中一个非常好用的东西Tuple,自从知道了它之后,发现我的代码里面到处都是它的身影。那么Tuple究竟是何物呢?

       在知道Tuple之前,我经常碰到要使用一些记录的集合,如果每条记录只有一个元素,一般都是用List来存储,如果每条记录,我想记录两个属性,我一般用Dictionary<int,int>来存储,但是如果每个记录要记录三个属性,甚至四个属性的时候,那么我们只能定义一个数据结构或者实体类来进行存储,然后再把这个实体装到List中去。但是大多数时候,这么一个List的列表只是临时用一下,所以如果再去定义一个数据结构,就觉得有点不划算,那该怎么办呢?这时Tuple就该上场了。

      Tuple是元组的意思,它有八种形式(注意这里并不是说Tuple类有八种构造函数,而是八个不同的类,每种形式都是一个单独的类),它里面可以装任意多的元素,它的形式如下:

image

      如果你的元素个数小于8,那么你可以直接调用相应的构造函数,如果你的元素个数大于8,那也不要紧,我们可以看到最后一个构造函数的最后一个参数是TRest,TRest也是Tuple类型的,这就说明,你可以在Tuple里面加入任意多的元素。

      下面以Tuple<T1>为例,来看看它里面具体的内容,它的内部代码如下:

[Serializable]
public class Tuple<T1> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple
{
    private readonly T1 m_Item1;
    public T1 Item1
    {
        get
        {
            return this.m_Item1;
        }
    }
    int ITuple.Size
    {
        get
        {
            return 1;
        }
    }
    public Tuple(T1 item1)
    {
        this.m_Item1 = item1;
    }
    public override bool Equals(object obj)
    {
        return ((IStructuralEquatable)this).Equals(obj, EqualityComparer<object>.Default);
    }
    bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
    {
        if (other == null)
        {
            return false;
        }
        Tuple<T1> tuple = other as Tuple<T1>;
        return tuple != null && comparer.Equals(this.m_Item1, tuple.m_Item1);
    }
    int IComparable.CompareTo(object obj)
    {
        return ((IStructuralComparable)this).CompareTo(obj, Comparer<object>.Default);
    }
    int IStructuralComparable.CompareTo(object other, IComparer comparer)
    {
        if (other == null)
        {
            return 1;
        }
        Tuple<T1> tuple = other as Tuple<T1>;
        if (tuple == null)
        {
            throw new ArgumentException(Environment.GetResourceString("ArgumentException_TupleIncorrectType", new object[]
            {
                base.GetType().ToString()
            }), "other");
        }
        return comparer.Compare(this.m_Item1, tuple.m_Item1);
    }
    public override int GetHashCode()
    {
        return ((IStructuralEquatable)this).GetHashCode(EqualityComparer<object>.Default);
    }
    int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
    {
        return comparer.GetHashCode(this.m_Item1);
    }
    int ITuple.GetHashCode(IEqualityComparer comparer)
    {
        return ((IStructuralEquatable)this).GetHashCode(comparer);
    }
    public override string ToString()
    {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.Append("(");
        return ((ITuple)this).ToString(stringBuilder);
    }
    string ITuple.ToString(StringBuilder sb)
    {
        sb.Append(this.m_Item1);
        sb.Append(")");
        return sb.ToString();
    }

      从上面的代码中可以看出几点,一是它实现了四个接口,最主要是ITuple这个接口,只要实现了这个接口,那么它就是Tuple的一个实例。第二个,我们可以看出来,它里面的元素是只读的,也就是说不能对里面的元素进行赋值,如果想得到这个元素的值,那么就直接调用对象的Item属性,Item1代表第一个元素的值,Item2代表第二个元素的值,依此类推。

      除了上面的八种Tuple类,Tuple还有一个静态类,在这个静态类里面有一个Create方法,这个方法有8种重载,下面看一下静态的Tuple类:

public static class Tuple
{
    public static Tuple<T1> Create<T1>(T1 item1)
    {
        return new Tuple<T1>(item1);
    }
    public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2)
    {
        return new Tuple<T1, T2>(item1, item2);
    }
    public static Tuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3)
    {
        return new Tuple<T1, T2, T3>(item1, item2, item3);
    }
    public static Tuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 item1, T2 item2, T3 item3, T4 item4)
    {
        return new Tuple<T1, T2, T3, T4>(item1, item2, item3, item4);
    }
    public static Tuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
    {
        return new Tuple<T1, T2, T3, T4, T5>(item1, item2, item3, item4, item5);
    }
    public static Tuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
    {
        return new Tuple<T1, T2, T3, T4, T5, T6>(item1, item2, item3, item4, item5, item6);
    }
    public static Tuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
    {
        return new Tuple<T1, T2, T3, T4, T5, T6, T7>(item1, item2, item3, item4, item5, item6, item7);
    }
    public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
    {
        return new Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>>(item1, item2, item3, item4, item5, item6, item7, new Tuple<T8>(item8));
    }
    internal static int CombineHashCodes(int h1, int h2)
    {
        return (h1 << 5) + h1 ^ h2;
    }
    internal static int CombineHashCodes(int h1, int h2, int h3)
    {
        return Tuple.CombineHashCodes(Tuple.CombineHashCodes(h1, h2), h3);
    }
    internal static int CombineHashCodes(int h1, int h2, int h3, int h4)
    {
        return Tuple.CombineHashCodes(Tuple.CombineHashCodes(h1, h2), Tuple.CombineHashCodes(h3, h4));
    }
    internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5)
    {
        return Tuple.CombineHashCodes(Tuple.CombineHashCodes(h1, h2, h3, h4), h5);
    }
    internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6)
    {
        return Tuple.CombineHashCodes(Tuple.CombineHashCodes(h1, h2, h3, h4), Tuple.CombineHashCodes(h5, h6));
    }
    internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7)
    {
        return Tuple.CombineHashCodes(Tuple.CombineHashCodes(h1, h2, h3, h4), Tuple.CombineHashCodes(h5, h6, h7));
    }
    internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8)
    {
        return Tuple.CombineHashCodes(Tuple.CombineHashCodes(h1, h2, h3, h4), Tuple.CombineHashCodes(h5, h6, h7, h8));
    }

   所以我们在使用的时候,可以有两种方式,一种是通过创建Tuple对象,另外一种就是通过静态类的Create方法。下面看一下具体的使用示例:

//Tuple中的值都是只读的,只能通过构造函数赋值
 
//创建一个元素的Tuple元组
Tuple<int> tuple = new Tuple<int>(5);
//Tuple<int> tuple = Tuple.Create(5);//等同于第一种方式
int a = tuple.Item1;
 
//创建三个不同类型的元组
Tuple<int, string, float> t = new Tuple<int, string, float>(1,"Tom",99.85f);
//Tuple<int, string, float> t = Tuple.Create(1, "Tom", 99.85f);//等同于第一种方式
int grade = t.Item1;
string name = t.Item2;
float score = t.Item3;
 
//还可以定义Tuple的集合
List<Tuple<int, string, float>> tupleList = new List<Tuple<int, string, float>>();

    看了上面的用法,是不是觉得很方便,不过还有一点需要注意的是,如果想创建8个元素的Tuple,可以使用下面的方法:

Tuple<int, int, int, int, int, int, int, Tuple<int>> tu1 = new Tuple<int, int, int, int, int, int, int, Tuple<int>>(1,2,3,4,5,6,7,new Tuple<int>(8));

     因为最后一个元素必须是ITuple类型的,所以在使用的时候一定要小心。如果用下面的这种方式就会报错:

//使用的时候会报错,因为最后一个参数必须是Tuple实例类型的
var tu2 = new Tuple<int, int, int, int, int, int, int, int>(1, 2, 3, 4, 5, 6, 7,8);

     可以看一下它的构造函数里面是怎样实现的:

public Tuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest)
{
    if (!(rest is ITuple))
    {
        throw new ArgumentException(Environment.GetResourceString("ArgumentException_TupleLastArgumentNotATuple"));
    }
    this.m_Item1 = item1;
    this.m_Item2 = item2;
    this.m_Item3 = item3;
    this.m_Item4 = item4;
    this.m_Item5 = item5;
    this.m_Item6 = item6;
    this.m_Item7 = item7;
    this.m_Rest = rest;
}

     它在构造函数里面经过了判断,如果不是ITuple类型就会抛出异常。不过可以使用静态Tuple类的Create方法来创建,代码如下:

//使用静态Tuple类的Create方法,最后一个参数不需要是Tuple类型的
var tu3 = Tuple.Create<int, int, int, int, int, int, int, int>(1,2,3,4,5,6,7,8);

     究其所以然,来看看Create方法里面有什么诡异之处:

public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
{
    return new Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>>(item1, item2, item3, item4, item5, item6, item7, new Tuple<T8>(item8));
}

     可以看出,实际上第八个参数也需要是ITuple类型的,只不过是它在内部帮助我们转换了而已。

     Tuple介绍完了,是不是觉得很有用呢,赶快在你的程序中体验一下吧。

     主要参考了《你必须知道的.net》里面的内容,所以跟书里面的介绍很类似,但绝不是抄袭,自己按照他的思路一步步实验过。很佩服anytao,以后争取先自己分析,再来阅读书上的内容。

原文地址:https://www.cnblogs.com/xiaoxiangfeizi/p/2790159.html