发布日期:2009.06.01 作者:Anytao 
© 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处。

Tuple,是函数式编程的概念之一,早见于Elang、F#等动态语言。不过,我第一次听说Tuple还早在2005年园子的Ninputer大牛提出在.NET 2.0实现Tuple的基本想法,我们可以通过以下地址仰慕当时的历史片段: 
探讨.NET 2.0中Tuple的实现方法 
由此可见,Tuple不是.NET 4.0的创造发明,但却是C#趋于函数式编程概念的必要补充。那么,我们首先来看看,什么是Tuple?

Tuple为何物?

什么是Tuple,在汉语上我们将其翻译为元组。Tuple的概念源于数学概念,表示有序的数据集合。在.NET中Tuple被实现为泛型类型,n-Tuple表示有n个元素的Tuple,集合的元素可以是任何类型,例如定义一个3-Tuple表示Date(Year, Month, Day)时可以定义为:

// Release : code01, 2009/05/29
// Author : Anytao, http://www.anytao.com
var date = Tuple.Create<int, int, int>(2009, 5, 29);

通过Tuple.Create<int, int, int>将定义一个Tuple<int, int, int>实例,该实例实现三个数据成员:

对于Tuple的具体解析我们随后分析,当下仅了解一个大致。

我们可以有两个方面的理解,在.NET中关于Tuple我们有如下的定义:

  • 广义上, Tuple就是一种数据结构,通常情况下,其成员的类型及数据是确定的。
  • 狭义上,凡是实现了ITuple接口的类型,都是Tuple的实例。在.NET 4.0 BCL中,预定义了8个Tuple类型。例如最简单的Tuple定义为:
public class Tuple<T1> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple
{
}

其他所有的Tuple类型都实现了ITuple接口,该接口被定义为:

interface ITuple
{
int Size { get; } int GetHashCode(IEqualityComparer comparer);
string ToString(StringBuilder sb);
}

在该接口中,定义了一个只读属性Size、两个覆写方法GetHashCode和ToString,实现该接口的Tuple八大金刚如下:

public class Tuple<T1>
public class Tuple<T1, T2>
public class Tuple<T1, T2, T3>
public class Tuple<T1, T2, T3, T4>
public class Tuple<T1, T2, T3, T4, T5>
public class Tuple<T1, T2, T3, T4, T5, T6>
public class Tuple<T1, T2, T3, T4, T5, T6, T7>
public class Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>

注:Size属性、ToString(StringBuilder sb)方法,均被实现为显示接口方法,所以只能以接口实例访问,不过ITuple本身被定义internal,意味着我们无法在程序中访问ITuple,何意何解尚不明确。

在下面的定义中,我们将Custom Request封装为Tuple:

// Release : code02, 2009/05/29
// Author : Anytao, http://www.anytao.com
public class MyRequest
{
public Tuple<string, Uri, DateTime> GetMyRequest()
{
return Tuple.Create<string, Uri, DateTime>("anytao.com", new Uri("http://anytao.net/"), DateTime.Now);
}
}

为什么要用Tuple呢?这是个值得权衡的问题,上述MyRequest类型中通过3-Tuple对需要的Request信息进行封装,我们当然也可创建一个新的struct来封装,两种方式均可胜任。然则,在实际的编程实践中,很多时候我们需要一种灵活的创建一定数据结构的类型,很多时候新的数据结构充当着“临时”角色,通过大动干戈新类型完全没有必要,而Tuple既是为此种体验而设计的。例如:

  • Point {X, Y},可以表示坐标位置的数据结构。
  • Date {Year, Month, Day},可以表示日期结构;Time {Hour, Minute, Second},可以表示时间结构;而DateTime {Date, Time}则可以实现灵活的日期时间结构。
  • Request {Name, URL, Result},可以表示Request的若干信息。
  • 。。。,随需而取。

Tuple inside

为了对Tuple一探究竟,我们使用Reflector工具打开神秘之门,就实现而言,Tuple类型略显单薄,并没有什么“神奇”的设计,以Tuple<T1, T2>而言,我们可以看到其部分实现:

[Serializable]
public class Tuple<T1, T2> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple
{
// Fields
private T1 m_Item1;
private T2 m_Item2; // Methods
public Tuple(T1 item1, T2 item2)
{
this.m_Item1 = item1;
this.m_Item2 = item2;
} string ITuple.ToString(StringBuilder sb)
{
sb.Append(this.m_Item1);
sb.Append(", ");
sb.Append(this.m_Item2);
sb.Append(")");
return sb.ToString();
} int ITuple.Size
{
get
{
return 2;
}
} // Properties
public T1 Item1
{
get
{
return this.m_Item1;
}
} public T2 Item2
{
get
{
return this.m_Item2;
}
} //More and more...
}

其他的Tuple类型也大致如此,所以我们易于知晓Item1、Item2、…、ItemN是如何被定义的,同时也纳闷Size属性将何去何从,也打消了我们期望通过foreach来遍历Tuple元素的可能,未来如何,只有期待。

不过,对于Tuple而言,因为其元素数量的有限性,虽然能够满足大部分的需求,当时动态体验是我们越来越期望的编程体验。同时,尤其注意public class Tuple<T1, T2, T3, T4, T5, T6, T7, TRest> 引发的可能ArgumentException,例如:

// Release : code03, 2009/05/31
// Author : Anytao, http://www.anytao.com
var t8 = Tuple.Create<int, int, int, int, int, int, int, int>(1, 2, 3, 4, 5, 6, 7, 8);
Console.WriteLine(t8.Rest);

将引发异常:

Unhandled Exception: System.ArgumentException: The last element of an eight element tuple must be a Tuple.

提示我们最后的TRest应该为Tuple,所以修改程序为:

// Release : code04, 2009/05/31
// Author : Anytao, http://www.anytao.com
var trest = Tuple.Create<int>(8);
var t8 = Tuple.Create<int, int, int, int, int, int, int, Tuple<int>>(1, 2, 3, 4, 5, 6, 7, trest);
Console.WriteLine(t8.Rest);

则没有任何问题,究其原因我们很容易从Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>构造方法中找到答案:

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

TRest类型参数必须被实现为ITuple,否则引发异常。TRest在某种程度上为元素的扩展带来点方便,但是我仔细想来,总觉此处TRest的设计有点多此一举,既然是类型参数,T1、T2、…、TN其实均可为ITuple实例,何必非拘泥于最后一个。

优略之间

当前,.NET 4.0预定义的Tuple类型仅有8个,所以我们应考虑对于Tuple提供适度扩展的可能, 然而遗憾的是ITuple类型被实现为internal,所以我们无法继承ITuple,只好自定义类似的实现:

优势所在:

  • 为方法实现多个返回值体验,这是显然的,Tuple元素都可以作为返回值。
  • 灵活的构建数据结构,符合随要随到的公仆精神。
  • 强类型。

不足总结:

  • 当前Tuple类型的成员被实现为确定值,目前而言,还没有动态决议成员数量的机制,如果你有可以告诉我:-)
  • public class Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>,可能引发ArgumentException

参考文献

[你必须知道的.NET]第三十二回,,深入.NET 4.0之,Tuple一二的更多相关文章

  1. [你必须知道的.NET]第三十回:.NET十年(下)

    发布日期:2009.05.11 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. /// <summary> /// 本文部分内容,已 ...

  2. [你必须知道的.NET]第三十五回,判断dll是debug还是release,这是个问题

    发布日期:2009.12.29 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 问题的提出 晚上翻着群里的聊天,发现一个有趣的问题:如何通过编码 ...

  3. [你必须知道的.NET]第三十四回,object成员,不见了!

    发布日期:2009.10.30 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 在.NET世界了,object是公认的造物主,其麾下的7大成员, ...

  4. [你必须知道的.NET]第三十一回,深入.NET 4.0之,从“新”展望

    发布日期:2009.05.22 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. /// <summary> /// 本文开始,将以& ...

  5. [你必须知道的.NET]第三十三回,深入.NET 4.0之,Lazy<T>点滴

    发布日期:2009.10.29 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 对象的创建方式,始终代表了软件工业的生产力方向,代表了先进软件技 ...

  6. [你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理

    原文地址:http://kb.cnblogs.com/page/42318/ 系列文章导航: [你必须知道的.NET] 开篇有益 [你必须知道的.NET] 第一回:恩怨情仇:is和as [你必须知道的 ...

  7. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    发布日期:2009.02.25 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)], ...

  8. 《你必须知道的.NET》读书笔记三:体验OO之美

    此篇已收录至<你必须知道的.Net>读书笔记目录贴,点击访问该目录可以获取更多内容. 一.依赖也是哲学 (1)本质诠释:“不要调用我们,我们会调用你” (2)依赖和耦合: ①无依赖,无耦合 ...

  9. [你必须知道的.NET]第二十九回:.NET十年(上)

    发布日期:2009.05.08 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. /// <summary> /// 本文部分内容,已 ...

随机推荐

  1. golang的哪些坑爷事: package实践

    在golang中package是个困惑的概念, 特别是package还可以与folder不同名, 委实让我恶心了一把. 关于golang的package的最佳实践: package is folder ...

  2. hi3531播放1080p60f, 延迟越来越大的问题与解决办法

    问题 hi3531播放1080p60f, 延迟越来越大 左边屏幕是ffplay播放的,右边屏幕是3531播放的 数据是udp组播 mpegts, h264 12M码流 原因 经过测试发现: 解码器中缓 ...

  3. SQLiteAPI函数详解

    使用的过程根据使用的函数大致分为如下几个过程: sqlite3_open() sqlite3_prepare() sqlite3_step() sqlite3_column() sqlite3_fin ...

  4. Leetcode-Construct Binary Tree from inorder and preorder travesal

    Given preorder and inorder traversal of a tree, construct the binary tree. Note: You may assume that ...

  5. 11.3Daily Scrum

    人员 任务分配完成情况 明天任务分配 王皓南 实现网页上视频上传的功能,研究相关的代码782 数据库测试 申开亮 实现网页上视频浏览的功能.研究相关的代码和功能.783 实现视频浏览的功能 王宇杰 负 ...

  6. 小组开发项目NABC分析

    我们团队的开发项目为:重量解锁 是根据重力感应实现手机的解锁方式,在传统滑屏的基础上我们想增添新的形式,实现用户用一组动作就能实现手机解锁功能,更加方便,炫酷. NABC模型 1.N:我们的创意在使用 ...

  7. Mac下的常用Shell命令

    今天介绍一下在Mac的终端中一些常用的Shell命令: 1.查看当前工作目录的完整路径 pwd (pwd的原意是:print work directiory,而不是密码password的意思,呵呵) ...

  8. TF-IDF与余弦相似性的应用(二):找出相似文章

    上一次,我用TF-IDF算法自动提取关键词. 今天,我们再来研究另一个相关的问题.有些时候,除了找到关键词,我们还希望找到与原文章相似的其他文章.比如,"Google新闻"在主新闻 ...

  9. 2015最新百度搜索引擎(seo优化)排名算法

    多少年来,对于弄清百度排名算法成为了一代又一代站长的最高目标.随着百度推出了搜索引擎网页质量**,直接揭开了神秘的百度排名算法,这是作为站长福音啊.现在小编就来为大家介绍一下. 首先想要得到直接需要的 ...

  10. 【Longest Common Prefix】cpp

    题目: Write a function to find the longest common prefix string amongst an array of strings. 代码: class ...