最近发现了.NET4.0中一个非常好用的东西Tuple,自从知道了它之后,发现我的代码里面到处都是它的身影。那么Tuple究竟是何物呢?
在知道Tuple之前,我经常碰到要使用一些记录的集合,如果每条记录只有一个元素,一般都是用List来存储,如果每条记录,我想记录两个属性,我一般用Dictionary<int,int>来存储,但是如果每个记录要记录三个属性,甚至四个属性的时候,那么我们只能定义一个数据结构或者实体类来进行存储,然后再把这个实体装到List中去。但是大多数时候,这么一个List的列表只是临时用一下,所以如果再去定义一个数据结构,就觉得有点不划算,那该怎么办呢?这时Tuple就该上场了。
Tuple是元组的意思,它有八种形式(注意这里并不是说Tuple类有八种构造函数,而是八个不同的类,每种形式都是一个单独的类),它里面可以装任意多的元素,它的形式如下:
如果你的元素个数小于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介绍完了,是不是觉得很有用呢,赶快在你的程序中体验一下吧。