探讨.NET 2.0中Tuple的实现方法

我在介绍Visual Basic 9.0的时候,曾经多次提到Tuple这个概念,当时是作为匿名类型的实例出现的。现在我们单独来讨论一下这个概念。Tuple常常译为“组元”,在大部分支持Tuple的语言中,常常表示成员数目确定,每个成员类型也确定的结构。常常用于表示函数的多个返回值或者查询的结果等。Tuple应当是强类型的,即所有成员的类型在编译时确定。比如,假想语法下

Dim t = New Tuple(Of String, Integer, Double)

那么t将具有三个成员,该数目无法改变;同时三个成员的类型分别为String, Integer和Double,也无法改变。如你所见,Tuple可以看作不用事先声明的结构体,可以根据所使用的场合灵活地创建。那么VB9和C#3的匿名类型当然是Tuple很好的实现方案。但是这都是N年后的东西了,我们在.NET 2.0中能否实现Tuple?最关键的难点在于,我们要在希望使用的地方创建Tuple的结构,而不是事先声明,因此就必须有个灵活的机制来完成。

方法一:TypeList

我是某一天在公共汽车上想到这个办法,后来看到和Loki的TypeList有相似之处。当然.NET没有特化和记录类型的能力,所以无法实现TypeList。但我们把静态类型运算的思路移到运行时,就可以做Typed Variable List——那就是Tuple。

public abstract class TypeNode { internal TypeNode {} }

public sealed class Tail : TypeNode { }

public sealed class Tuple<T, TNode> : TypeNode where TNode : TypeNode, new()
{
    public T Field = default(T);
    public TNode Next = new TNode();
}

我充分利用了.NET泛型的约束特性来达成我的设计。TypeNode被设计为abstract,因此约束了new()的泛型参数TNode将无法取值TypeNode本身的类型。而其internal的构造函数又限制了用户继承于它。这个手法就将TNode的取值范围限定在Tail和Tuple两个类型上。这个用法是我认为约束用法中相当巧妙的一种。

这个类型的原理很简单,就是利用泛型,在创建TypeList的实例时自动生成相同结构的链表。比如我们要创建一个String, Integer, Double的Tuple,就是这样写:

Tuple<string, Tuple<int, Tuple<double, Tail>>> t;

如你所见,这种Tuple的类型参数第一个是某节点的类型,第二个要么是另一个Tuple,要么是Tail(表示终结列表)。这个对象创建出来以后就会自动生成一个“各个节点类型都不相同”的链表。

t = new Tuple<string, Tuple<int, Tuple<double, Tail>>>();
t.Field = " a string ";
t.Next.Field = 123;
t.Next.Next.Field = 13.56;

Tail没有Next字段,因此遇到Tail就代表Tuple终结了,这可以由编译器检查,因此没有越界的危险。而且这种Tuple可以达到无限长。不过这种方法也是有缺陷的,首先使用的语法方面非常不便,如果要用第7个字段,要写成myTuple.Next.Next.Next.Next.Next.Next.Field,稍不注意就会写错。无论VB还是C#都没有足够的抽象能力简化这一操作。第二个缺陷是建立Tuple时的一连串new操作开销很大,因为这里的new是通过反射进行的。所以受限于语言特性的缺乏,这种方法无法达到很完美的地步,不过这个思路也许在其他场合可以用上。

方法二:重载原型

模仿泛型委托的思路,我们可以用完全泛型化的一系列同名结构来模拟即时创建的Tuple:

struct Tuple<T0>
{
    public T0 Field0;
}

struct Tuple<T0, T1>
{
    public T0 Field0;
    public T1 Field1;
}

struct Tuple<T0, T1, T2>
{
    public T0 Field0;
    public T1 Field1;
    public T2 Field2;
}
......
struct Tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> {...}

这样就创建了一组Tuple结构。因为名称相同,在使用中不会察觉到存在10个类型,而是“要什么有什么”:

Tuple<string, int, double> t1;
Tuple<string, string> t2;
Tuple<int, int, int, int, float> t3;
......

这和我们一开始假想的语法一样!而且没有任何额外开销,相当完美。但是它的元素数目有限,一开始定义了几个就只能有几个,好在一般不需要太多,10个够用了。不过这样生成的Tuple有点死板,似乎没有什么可以智能化的地方。

我将在我的VBF中采用第二种Tuple方案,斟酌后还是觉得它比较实际。唯一改动的地方就是为每个Tuple结构增加了一个初始化所有成员的构造函数。

时间: 2024-10-31 01:13:40

探讨.NET 2.0中Tuple的实现方法的相关文章

水晶报表在VC++6.0中的简单使用方法

水晶报表是一个报表设计开发的强大工具,功能强大,设计灵活,在水晶报表光盘中只提供了一个完全动态生成报表的例子,使用繁琐.现介绍其在VC++6.0中的简单使用方法.编译环境:VC++6.0 sp5 .Windows 2000 Server sp3 (en).一.导入水晶报表使用的动态联接库:根据实际修改文件路径. #import "C:\Documents and Settings\Administrator\桌面\cr\craxdrt9.dll" no_namespace 二.定义接口

在asp.net 2.0中使用存储过程的方法

本文介绍了在asp.net 2.0中使用存储过程的方法. 以下是SQL中两个存储过程: 以下是引用片段: CREATE PROCEDURE dbo.oa_selectalluser AS select * from UserInfo GO CREATE PROCEDURE dbo.oa_SelectByID @id int AS select * from UserInfo where ID=@id GO 一个是带参数的存储过程,一个是不带参数的存储过程.下面介绍怎么在VS2005中使用这两个存

在ASP.NET 2.0中数据绑定的实现方法

1.为什么ASP.NET 2.0中的数据绑定控件不需要写代码就能完成更新.删除.新建等数据操作? ASP.NET 1.x时,DataGrid等控件使用DataBinder.Eval(Container.DataItem,"ColumnName")这样的表达式可以将数据源中的数据绑定到控件上,但并不能在更新数据时自动将控件中的新值取出,更新回数据库.所以ASP.NET 2.0中的数据绑定分为两种:单向数据绑定(即表达式或ReadOnly设为True的BoundField,只提供从数据源到

一起谈.NET技术,在ASP.NET 2.0中数据绑定的实现方法

1.为什么ASP.NET 2.0中的数据绑定控件不需要写代码就能完成更新.删除.新建等数据操作? ASP.NET 1.x时,DataGrid等控件使用DataBinder.Eval(Container.DataItem,"ColumnName")这样的表达式可以将数据源中的数据绑定到控件上,但并不能在更新数据时自动将控件中的新值取出,更新回数据库.所以ASP.NET 2.0中的数据绑定分为两种:单向数据绑定(即表达式或ReadOnly设为True的BoundField,只提供从数据源到

.net4.0中tuple元组的使用方法

 Tuple是.NET 4.0的新特性,主要功能是动态返回数据结构,也可以用做临时数据结构.现在有了元组[Tuple],看看它怎么用 原来做一些功能时需要一个方法返回几个值,有两种方法:   1. 非常难看.难用的OUT参数:   2. 新写一个实体,太麻烦:   现在有了元组[Tuple],看看它怎么做:  代码如下: private void TestTuple() {     var test1 = Test1();     if (test1.Item3 == "Test1")

.net4.0中tuple元组的使用方法_实用技巧

原来做一些功能时需要一个方法返回几个值,有两种方法: 1. 非常难看.难用的OUT参数: 2. 新写一个实体,太麻烦: 现在有了元组[Tuple],看看它怎么做: 复制代码 代码如下: private void TestTuple(){    var test1 = Test1();    if (test1.Item3 == "Test1") {        //TODO.......    }} private Tuple<int, int, string> Test

【转】PB9.0中messagebox的使用方法(原创)

基本写法:Messagebox('标题','内容') 完整写法: MessageBox ( '标题','内容',图标,按键,默认值)            其中标题与内容为要显示的字符串,不可省略,但可以省略,即什么也不显示,例如Messagebox('','')这样也是正确的单里面的东西一样也不能少!图标可选值: Information! 或 StopSign! 或 Exclamation!(默认图标) 或Question! 或 None!按键可选值: OK! (默认方式)或 OKCancel

探讨JDBC 4.0在设计和性能方面的改进

本文将与你一起探讨JDBC 4.0中引入的新特征,讨论它对一些现有问题的解决方案,并且通过具体示例展示它在设计和性能方面的改进. 自从核心Java语言的第一个公开发行版本起,JDBC(Java数据库连接)已经经历了十年的发展历程.它的当前版本4.0(将与Java标准版本6.0一起打包发行)提供了一组更为丰富的API,主要目的在于改进软件开发的设计和性能. 本文将重点讨论JDBC 4.0规范在设计和性能方面的改进. 一.注释和泛型DataSet 在本文中,我假定你已经了解注释和泛型.其实,这两个概

C#3.0中的“多重继承”

C#的对象系统是个单根系统,不支持类的多继承,只支持多接口实现,这在 某种程度带来了一些不便:我们在系统设计时经常会抽象出一些接口,并为接口 提供一个抽象类作为默认的实现,然后实际使用的类可以从抽象类派生.如果一 个类实现了多接口,那我们只能选择一个抽象类作为祖先类,再将其他接口的实 现手工加到类中. 这种情况在C#3.0中有了变化,我们现在可以利用C#3.0的扩展方法来实现一 个"受限的多继承". C#3.0中引入了扩展方法,可以利用一个静态类的静态方法为一个类或者接口 添加方法,关