InfoQ

文章

通过索引器简化C#类型信息访问

作者 王翔(Vision Wang) 发布于 2008年7月13日 上午6时56分

社区
.NET
主题
.NET框架
标签
LINQ

前言

作为一个有别于Java、Ruby等语言的一个特性,C#可以用索引器(Indexer)将类型本身以对象数组的形式供外部使用。下面是一个对比:(为了简化,略去了边界检查)

相关厂商内容

从卓越工程角度看微软中国开发团队的成长

免费迷你书下载:开源技术选型手册

Jimmy Nilsson谈LINQ to SQL

SOY Framework:Java富客户端快速开发框架

IDC:《软件商成长路线图》白皮书免费下载

相关赞助商

InfoQ中文站.NET社区,关注.NET和微软的其他企业开发解决方案,通过新闻、文章、视频访谈和演讲以及迷你书等为中国.NET社区提供一流资讯。

不难看出通过引入索引器,我们的目标类型DataAccess在涉及到根据编号检索的时候看上去更接近一个数组的样子,客户程序的开发人员不用关心具体的方法名称是GetData/GetInternalData或者是SetData/ UpdateData,仅仅把它当成一个数组就可以了,编码上也更加简洁、直观。

但索引器提供的检索能力不仅单纯面向一个一维数组,我们可以用它检索多维数组、内存空间数据(Space Data, 2D或3D,如果您的应用是面向时空穿梭的话,完全可以扩展至4D)、复杂的数据结构(树、图、语义网络)、多因素的计算结果… … 只要类型的封装人员和使用人员就每个索引项的含义达成一致即可。例如:下面是对法定公历节日信息访问的类型:

是不是感觉上访问GregorianCalendarFixture的时候就像我们面对着一个DataTimePicker控件一样?

实现多途径的数据项检索

上面都是单个途径看目标类型的数据组织,但就像我们看待一个人一样,评价它往往会通过不同途径,同时每个途径可能也是一组对象或一个复杂的数据结构系统,而且每个检索的指标是不同的数据类型。同样出于封装和简化客户程序使用的考虑,我们可以继续用索引器完成。

比如:我们希望提供按照城市ID、中文拼音缩写、城市名称枚举3种方式获得其气温情况,为了便于客户程序使用,我们重载出了一系列索引器,这样一方面客户程序可以根据上下文用不同类型的键值访问,也省去了解具体应该使用哪个函数的繁琐。

实现类似RDBMS中联合主键或唯一性索引的访问

通过“索引器”这个名称我们会很自然的联系到RDBMS(关系数据库)中的索引,就如我们在设计数据库逻辑结构的过程一样,往往为了唯一标注每条记录,常常会用到主键或唯一性索引,而构成他们的属性(列)可能是1项也可能是几项的联合。.NET平台为了跨层调用的方便,从一开始就支持离线的DataSet和基于DOM的XML解析数据,随着.NET平台升级到2.0,对象化的配置类型也可以提供基于内存缓冲信息的访问。应用可能要求包装类型提供基于联合索引的查询(尤其对于属性较多、关系复杂的实体),而索引器又成了一个非常优雅的封装方式。

比如:一个员工实体包括“FirstName”、“FamilyName”、“Title”三个属性,我们需要包装一个Staff类型管理全部的员工信息。

同时根据UI绑定或其他功能检索的需要,我们会根据他的联合主键(FirstName + FamilyName)提供一个索引器,用它访问具体的员工记录。示例如下:

通过委托传递索引规则

如上文,对于检索规则固定的情况而言,我们可以通过在索引器内部硬编码完成,但如果要完成一些更为公共的类库,我们往往还需要“授之以渔”,即除了告诉他“要检索”这个任务之外,还要把检索策略和规则告诉它。这方面C#是非常有优势的,因为它有对象化的托管委托类型(delegate),而且.NET Framework FCL部分也提供了很多现成的委托,所以我们不妨善加利用。

这时候,我们会发现索引器的功能更加强大,我们就像在使用SQL语句的WHERE 子句一样,以灵活的方式对目标数据根据需要筛选。

不过,我们在实际使用中WHERE子句可能还会包括不只一条的限制条件,索引器一样可以完成。例如,定义为下列形式:

LINQ时代的索引器

乍一看,索引器似乎已经越来越接近于LINQ通过Lamada表达式完成的功能,不过有些区别:

  • 定位上索引器一般面向单条检索结果,而不是批量结果(尽管我们可以让索引器返回一个IEnumerable
  • 从封装和客户程序使用的角度看,LINQ有各种内置并被优化的LINQ to系列,而索引器给客户程序是一种更为贴近业务语义、更加直观的形式,因为客户程序无需写LINQ查询,只是按照键值检索即可

不过,把两者结合使用倒是一个非常不错的组合,索引器做接口、LINQ完成内部检索逻辑,客户程序在无需记住具体方法名称的前提下,按照键值检索即可,索引器内部则依托LINQ to系列的基础,提供对各种异构数据源的访问。

小结

就像我们设计接口时会根据业务领域,把类型的职能分解一样,操作类型的时候同样,一样可以根据访问内容不同,选择使用不同的访问方法,比如:

  • 索引器:承担各种检索和查找的工作
  • 属性(Property):承担“它的…特性是…”或“它们的…特质是…”的工作,用来标注某个实例特性(成员属性)或静态特性(静态属性)
  • 普通的方法:承担“让它处理……”的职能
  • 而事件定位于“当……发生的时候,需要作些……”

受到惯性影响,我们常常把索引器作为一个仅仅按照编号反馈结果的入口,但就如SQL 中的WHERE子句, 我们其实可以做很多。善用之,它会令我们的程序更加亲切、更加清晰。

3 条回复

回复

怎么说呢,我还是觉得接受Lambda Expression的索引器要慎用 发表人 Jeffrey Zhao 发表于 2008年7月14日 上午7时55分
Re: 怎么说呢,我还是觉得接受Lambda Expression的索引器要慎用 发表人 hello hello 发表于 2008年7月14日 下午9时36分
这样的代码适合个人项目 发表人 皓月 江天 发表于 2008年7月15日 下午9时31分
  1. 返回顶部

    怎么说呢,我还是觉得接受Lambda Expression的索引器要慎用

    2008年7月14日 上午7时55分 发表人 Jeffrey Zhao

    其实索引器和我们额外写的某个Get方法在内部实现上几乎完全相同,执行效果也一样,更没有比如性能上的差别。 但是索引器给人的感觉就是一个非常单纯的“取”操作,例如通过key查找value,通过数组的下标取元素等等。 而Lambda Expression实际上是包含语义的,可能表达的是复杂筛选操作;同样比如传入一个City对象,然后根据它的状态来获取一些东西(比如相关产品)。 对于这种情况,我在设计接口时更倾向于写一个Get方法而不是索引器……

  2. 其实很多开源软件中都很难看到索引器的使用,尤其是那些从Java移过来的。 经常使用索引器属与某个人编码个性有关,尤其对于后面传递委托的例子,感觉是纯粹为了秀语法,和要完成的功能关系不大。

  3. 返回顶部

    这样的代码适合个人项目

    2008年7月15日 下午9时31分 发表人 皓月 江天

    我敢说十人有八个人没有这种使用索引器的习惯,你的代码在团队中让其他人难以阅读,除非索引器用得非常有必要。

独家内容

构建的可伸缩性和达到的性能:一个虚拟座谈会

这个由业界主要专家们参加的座谈会探究了在使应用程序具备尽可能好的伸缩性及性能的过程中所面临的挑战和思考过程。

OpenSocial的分析与实现

本视频主要对OpenSocial进行了分析,并对实现的方式进行了介绍。其中包括:OpenSocial的开发经验、Container Provider的技术准备、平台的构成要素、具体的规范、以及对未来的展望。

缓存系统MemCached的Java客户端优化历程

Memcached在大型网站被应用得越来越广泛,但是Java客户端并不多,本文作者基于现有的开源客户端进行了封装优化,并翔实记录了这一过程。

超越SOA:动态业务应用的新企业应用框架(2)

在他们文章的第二部分,作者探讨了动态业务应用的架构并介绍了资源容器的概念。他们示范了如何在JEE之上构建这个架构,以及它如何影响实现生产力。

使用ClickOnce细分发布版本

ClickOnce让WinForms应用程序的部署轻而易举。David Cooksey演示了如何在ASP.NET中编写一个HttpHandler来实现对ClickOnce部署的版本细分。

敏捷教练,从A到Z

敏捷带来了新的领导者角色,“敏捷教练”。它是不是跟“部门经理”或“技术领导”一样,只是换汤不换药呢?教练Pat Kua在这篇启蒙文章中对敏捷教练一职做了概述。

利用Ruby简化你的Java测试(进阶篇)

本文是Productive Java with Ruby系列文章的第二篇,通过上一篇的介绍,我想大家对如何利用Ruby进行单元测试有了一个基本的了解,从这里开始,我将和大家一起讨论一些利用Ruby进行单元测试时的高级话题。

书评:《应用SOA》

《应用SOA》是由四位一流SOA专家合著关于SOA的新书,其主旨是帮助你成功地实施SOA。尤其是,这本书将帮助你把你的SOA项目与企业架构、IT治理、核心数据和BPM项目结合起来。