Delphi中的容器类(3)

TBucketList和TObjectBucketList类

  从Delphi6开始,VCL的Contnrs单元中又增加了两个新的容器类TBucketList和TObjectBucketList。TBucketList实际上也是一个简单基于哈希表的指针-指针对列表。接口定义如下:

 TBucketList = class(TCustomBucketList)

 public
  destructor Destroy; override;
  procedure Clear;
  function Add(AItem, AData: Pointer): Pointer;
  function Remove(AItem: Pointer): Pointer;
  function ForEach(AProc: TBucketProc; AInfo: Pointer = nil): Boolean;
  procedure Assign(AList: TCustomBucketList);
  function Exists(AItem: Pointer): Boolean;
  function Find(AItem: Pointer; out AData: Pointer): Boolean;
  property Data[AItem: Pointer]: Pointer read GetData write SetData; default;
 end;

  类的Add方法现在接受两个参数AItem和AData,我们可以把它看成是指针版的Map实现(从容器类来看, Delphi从语言的灵活性来说不如C++,为了实现不同类型的哈希Map容器,Delphi需要派生很多的类,而C++的Map是基于模版技术来实现的,容器元素的类型只要简单的声明一下就能指定了,使用起来非常方便。而从简单性来说,则不如Java的容器类,因为Delphi中的String是原生类型,而不是类,并且Delphi还提供对指针的支持,因此要为指针和字符串提供不同的Map派生类),类中的Exists和Find等方法都是通过哈希表来实现快速数据定位的。同时,同一般的列表容器类不同,TBucketList不提供通过整数下标获取列表中的元素的功能,不过我们可以使用ForEach方法来遍历容器内的元素。

  TObjectBucketList是从TBucketList派生的基类,没有增加任何新的功能,唯一的不同之处就是容器内的元素不是指针而是对象了,实现了更强的类型检查而已。

  其它容器类

  TThreadList类

  TThreadList类实际上就是一个线程安全的TList类,每次添加或者删除容易中指针时,TThreadList会调用EnterCriticalSection函数进入线程阻塞状态,这时其它后续发生的对列表的操作都会阻塞在那里,直到TThreadList调用UnLockList释放对列表的控制后才会被依次执行。在多线程开发中,我们需要使用TThreadList来保存共享的资源以避免多线程造成的混乱和冲突。还要注意的是TThreadList有一个Duplicates布尔属性,默认为True,表示列表中不能有重复的指针。设定为False将允许容器内有重复的元素。

  TInterfaceList类

  在Classes单元中,VCL还定义了一个可以保存接口的列表类。我们可以向列表中添加接口类型,这个类的操作方法同其它的列表类没有什么区别,只不过在内部使用TThreadList作为容器实现了线程安全。

  拟容器类TBits类

  在Classes.pas还有一个特殊的TBits类,接口定义如下:

 TBits = class

 public
  destructor Destroy; override;
  function OpenBit: Integer;
  property Bits[Index: Integer]: Boolean read GetBit write SetBit; default;
  property Size: Integer read FSize write SetSize;
 end;

  它可以按位储存布尔值,因此可以看成是一个原生的Boolean值的容器类,但是它缺少列表类的很多方法和特性,不能算是一个完整的容器,因此我们称它为拟容器类。

  在我们开发过程中,经常需要表示一些类似于开关的二元状态,这时我们用TBits来表示一组二元状态非常方便,同时TBits类的成员函数主要是用汇编语言写的,位操作的速度非常快。二元状态组的大小通过设定TBits类的Size属性来动态的调整,存取Boolean值可以通过下标来存取TBits类的Bits属性来实现。至于OpenBit函数,它返回第一个不为True的Boolean值的下标。从接口定义可以看出,TBits类接口非常简单,提供的功能也很有限,我猜测这只是Borland的研发队伍满足内部开发有限需要的类,并不是作为一个通用类来设计的,比如它没有开放内部数据存取的接口,无法获得内部数据的表达,进而无法实现对状态的保存和加载等更高的需求。

  TCollection类

  前面我们提到了Delphi的IDE能够自动将字符串列表保存在DFM文件中,并能在运行时将设计期编辑的字符串列表加载进内存(也就是我们通常所说的类的可持续性)。TStrings这种特性比较适合于保存一个对象同多个字符串数据之间关联,比较类似于现实生活中一个人同多个Email账户地址之间的关系。但是,TStrings类型的属性有一个很大的局限那就是,它只能用于设计时保存简单的字符串列表,而不能保存复杂对象列表。而一个父对象同多个子对象之间的聚合关系可能更为常见,比如一列火车可能有好多节车厢构成,每节车厢都有车厢号,车厢类型(卧铺,还是硬座),车厢座位数,车厢服务员名称等属性构成。如果我们想在设计期实现对火车的车厢定制的功能,并能保存车厢的各个属性到窗体文件中,则车厢集合属性定义为TStrings类型的属性是行不通的。

  对于这个问题,Delphi提供了TCollection容器类属性这样一个解决方案。TCollection以及它的容器元素TCollectionItem的接口定义如下:

 TCollection = class(TPersistent)
 …
 protected
  procedure Added(var Item: TCollectionItem); virtual; deprecated;
  procedure Deleting(Item: TCollectionItem); virtual; deprecated;
  property NextID: Integer read FNextID;
  procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); virtual;
  { Design-time editor support }
  function GetAttrCount: Integer; dynamic;
  function GetAttr(Index: Integer): string; dynamic;
  function GetItemAttr(Index, ItemIndex: Integer): string; dynamic;
  procedure Changed;
  function GetItem(Index: Integer): TCollectionItem;
  procedure SetItem(Index: Integer; Value: TCollectionItem);
  procedure SetItemName(Item: TCollectionItem); virtual;
  procedure Update(Item: TCollectionItem); virtual;
  property PropName: string read GetPropName write FPropName;
  property UpdateCount: Integer read FUpdateCount;
 public
  constructor Create(ItemClass: TCollectionItemClass);
  destructor Destroy; override;
  function Owner: TPersistent;
  function Add: TCollectionItem;
  procedure Assign(Source: TPersistent); override;
  procedure BeginUpdate; virtual;
  procedure Clear;
  procedure Delete(Index: Integer);
  procedure EndUpdate; virtual;
  function FindItemID(ID: Integer): TCollectionItem;
  function GetNamePath: string; override;
  function Insert(Index: Integer): TCollectionItem;
  property Count: Integer read GetCount;
  property ItemClass: TCollectionItemClass read FItemClass;
  property Items[Index: Integer]: TCollectionItem read GetItem write SetItem;
 end;
 TCollectionItem = class(TPersistent)

 protected
  procedure Changed(AllItems: Boolean);
  function GetOwner: TPersistent; override;
  function GetDisplayName: string; virtual;
  procedure SetCollection(Value: TCollection); virtual;
  procedure SetIndex(Value: Integer); virtual;
  procedure SetDisplayName(const Value: string); virtual;
 public
  constructor Create(Collection: TCollection); virtual;
  destructor Destroy; override;
  function GetNamePath: string; override;
  property Collection: TCollection read FCollection write SetCollection;
  property ID: Integer read FID;
  property Index: Integer read GetIndex write SetIndex;
  property DisplayName: string read GetDisplayName write SetDisplayName;
 end;

  TCollection类是一个比较复杂特殊的容器类。但是初看上去,它就是一个TCollectionItem对象的容器类,同列表类TList类似,TCollection类也维护一个TCollectionItem对象索引数组,Count属性表示容器中包含的TCollectionItem的数目,同时也提供了Add和Delete方法来添加和删除TCollectionItem对象以及通过下标存取TCollectionItem的属性。看上去和容器类区别不大,但是在VCL内部用于保存和加载组件的TReader和TWriter类提供了两个特殊的方法WriteCollection和ReadCollection用于加载和保存TCollection类型的集合属性。IDE就是通过这两个方法实现对TCollection类型属性的可持续性。

  假设现在需要设计一个火车组件TTrain,TTrain组件有一个TCollection类型的属性Carriages表示多节车厢构成的集合属性,每个车厢则对应于集合属性的元素,从TCollectionItem类继承,有车厢号,车厢类型(卧铺,还是硬座),车厢座位数,车厢服务员名称等属性,下面是我设计的组件的接口:

type
 //车厢类型,硬座、卧铺
 TCarriageType = (ctHard, ctSleeper);
 //车厢类
 TCarriageCollectionItem = class(TCollectionItem)

 published
  //车厢号码
property CarriageNum: Integer read FCarriageNum write FCarriageNum;
//座位数
property SeatCount: Integer read FSeatCount write FSeatCount;
//车厢类型
property CarriageType: TCarriageType read FCarriageType write FCarriageType;
//服务员名称
  property ServerName: string read FServerName write FServerName;
 end;
 TTrain=class;
 //车厢容器属性类 
 TCarriageCollection = class(TCollection)
 *******
  FTrain:TTrain;
  function GetItem(Index: Integer): TCarriageCollectionItem;
  procedure SetItem(Index: Integer; const Value: TCarriageCollectionItem);
 protected
  function GetOwner: TPersistent; override;
 public
  constructor Create(ATrain: TTrain);
  function Add: TCarriageCollectionItem;
property Items[Index: Integer]: TCarriageCollectionItem read GetItem
write SetItem; default;
 end;
 //火车类
 TTrain = class(TComponent)
 *******
  FItems: TCarriageCollection;
  procedure SetItems(Value: TCarriageCollection);
 public
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;
 published
  property Carriages: TCarriageCollection read FItems write SetItems;
 end;

  其中车厢类的定义非常简单,只是定义了四个属性。而车厢集合类重定义了静态的Add方法以及Items属性,其返回结果类型改为了TCarriageCollectionItem,下面是车厢集合类的实现代码:

function TCarriageCollection.Add: TCarriageCollectionItem;
begin
 Result:=TCarriageCollectionItem(inherited Add);
end;
  
constructor TCarriageCollection.Create(ATrain: TTrain);
begin
 inherited Create(TCarriageCollectionItem);
 FTrain:=ATrain;
end;
  
function TCarriageCollection.GetItem(
 Index: Integer): TCarriageCollectionItem;
begin
 Result := TCarriageCollectionItem(inherited GetItem(Index));
end;
  
function TCarriageCollection.GetOwner: TPersistent;
begin
 Result:=FTrain;
end;
  
procedure TCarriageCollection.SetItem(Index: Integer;
 const Value: TCarriageCollectionItem);
begin
 inherited SetItem(Index, Value);
end;

  其中Add,GetItem和SetItem都非常简单,就是调用基类的方法,然后将基类的方法的返回结果重新映射为TCollectionItem类型。而构造函数中将TTrain组件作为父组件传入,并重载GetOwner方法,返回TTrain组件,这样处理的原因是IDE会在保存集合属性时调用集合类的GetOwner确认属性的父控件是谁,这样才能把集合属性写到DFM文件中时,才能存放到正确的位置下面,建立正确的聚合关系。

  而火车组件的实现也非常简单,只要定义一个Published Carriages属性就可以了,方法实现代码如下:

constructor TTrain.Create(AOwner: TComponent);
begin
 inherited;
 FItems := TCarriageCollection.Create(Self);
end;
  
destructor TTrain.Destroy;
begin
 FItems.Free;
 inherited;
end;
  
procedure TTrain.SetItems(Value: TCarriageCollection);
begin
 FItems.Assign(Value);
end;

  下面将我们的组件注册到系统面板上之后,就可以在窗体上放上一个TTrain组件,然后然后选中Object Inspector,然后双击Carriages属性,会显示系统默认的集合属性编辑器,使用Add按钮向列表中添加两个车厢,修改一下属性,如下图所示意:

 201002031701223335.jpg

从上面的属性编辑器我们,可以看到默认情况下,属性编辑器列表框是按项目索引加上一个横杠来显示车厢的名称,看起来不是很自然。要想修改显示字符串,需要重载TCarriageCollectionItem的GetDisplayName方法。修改后的GetDisplayName方法显示车厢加车厢号码:

function TCarriageCollectionItem.GetDisplayName: string;
begin
 Result:='车厢'+IntToStr(CarriageNum);
end;

  示意图:

 201002031701575453.jpg

保存一下文件,使用View As Text右键菜单命令察看一下DFM文件,我们会看到我们设计的车厢类的属性确实都被写到了DFM文件中,并且Carriages属性的父亲就是Train1:

 object Train1: TTrain
  Carriages = <
   item
    CarriageNum = 1
    SeatCount = 100
    CarriageType = ctHard
    ServerName = '陈省'
   end
   item
    CarriageNum = 2
    SeatCount = 200
    CarriageType = ctHard
    ServerName = 'hubdog'
   end>
  Left = 16
  Top = 8
 End

  TOwnedCollection

  从Delphi4开始,VCL增加了一个TOwnedCollection类,它是TCollection类的子类,如果我们的TCarriageCollection类是从TOwnedCollection类继承的,这时我们就不再需要向上面重载GetOwner方法并返回父控件给IDE,以便TCarriageCollection属性能出现在Object Inspector中了。

  总结

  本章中我介绍了几乎所有VCL中重要的容器类,其中TList及其子类相当于通用的容器类,虽然不如C++和Java功能那么强大,但是用好了已经足以满足我们90%的开发需要,而TStrings及其子类,还有TCollection则是实现所见即所得设计的关键类,对于开发灵活强大的自定义组件来说是必不可少的。

转载于:https://www.cnblogs.com/hackpig/archive/2010/02/15/1668527.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299458.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文读懂哈希和一致性哈希算法

哈希 Hash 算法介绍哈希算法也叫散列算法, 不过英文单词都是 Hash, 简单一句话概括, 就是可以把任意长度的输入信息通过算法变换成固定长度的输出信息, 输出信息也就是哈希值, 通常哈希值的格式是16进制或者是10进制, 比如下面的使用 md5 哈希算法的示例md5("123456"…

延迟开学?这些教育读书公众号可以帮助孩子学习! 你都关注了吗?

全世界只有3.14 % 的人关注了青少年数学之旅受新型冠状病毒疫情影响&#xff0c;日前&#xff0c;教育部印发《关于2020年春季学期延期开学的通知》。推迟开学时间&#xff0c;意味着寒假的延长。为此&#xff0c;小编精选这些优质的教育号和读书号帮助孩子学习&#xff01;理想…

go方法的深入探究(7.21增补)

2019独角兽企业重金招聘Python工程师标准>>> 1&#xff09;哪些类型可以有方法&#xff1a; 1&#xff09;只能对命名类型和命名类型的指针编写方法&#xff1b; 2&#xff09;不能对接口类型和接口类型的指针编写方法&#xff1b; 3&#xff09;只能在定义命名类型…

element文件上传有文件但是后台接收为空_程序员提高篇:大规格文件(G)是如何做分片优化的?...

作者&#xff1a;凹凸实验室 链接&#xff1a;https://juejin.im/post/5ebb4346e51d451ef53793ad整体思路第一步是结合项目背景&#xff0c;调研比较优化的解决方案。 文件上传失败是老生常谈的问题&#xff0c;常用方案是将一个大文件切片成多个小文件&#xff0c;并行请求接口…

你连简单的枚举类都不知道,还敢说自己会Java???滚出我的公司

枚举类型是Java 5中新增的特性&#xff0c;它是一种特殊的数据类型&#xff0c;之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束&#xff0c;但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。当需要定义一组常量时&#xff0c;强烈建议使…

绝对硬核!万物有“理”,科学原来如此有趣!

▲ 点击查看假如在生活中&#xff0c;你不小心将生鸡蛋和熟鸡蛋混在一起了&#xff0c;那么此时你要如何分辨&#xff0c;哪个鸡蛋是生的&#xff0c;哪个是熟的呢&#xff1f;假若你曾学过力学&#xff0c;那你一定能够轻易的分辨这个生熟问题。我们把这两个鸡蛋放在桌上&…

微软输入法2010下载使用-IME2010下载使用

3年前&#xff0c;写过IME2007的安装和使用&#xff0c;在Office 2010 beta开放之后&#xff0c;觉得单独把ime2010单独开放出来比较适合Office 2003/2007的用户群。 1。 依然还是和上次的IME2007提取方式一样&#xff0c;先用7-zip解压Office 2007 beta的exe文件&#xff1a;由…

理论修炼之RabbitMQ,消息队列服务的稳健者

????欢迎点赞 &#xff1a;???? 收藏 ⭐留言 ???? 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;????本文作者&#xff1a;由webmote 原创&#xff0c;首发于 【掘金】????作者格言&#xff1a;生活在于折腾&#xff0c;当你不折…

为什么年龄大了近视还增加_年龄明明一样大,为什么有人长得年轻,有人显老呢?...

台湾不老男神林志颖&#xff0c;始终是十几年前演偶像剧的脸。而德云社郭德纲与他是同龄人却饱经沧桑显得更加老相。这是一件让人哭笑不得的事&#xff0c;也被很多人编成段子。那么为什么有些人看起来年轻有些人却老的很快呢&#xff1f;哪一种更长寿呢&#xff1f;接下来让我…

利用Asp.net中的AJAX制作网页上自动选取开始日期及结束日期的用户自定义控件...

前段时间用过一个酒店入住预约网站&#xff0c;当你点击"入住时间"时会悬浮出一对并列的日历&#xff0c;然后点击左边的日历就能选择入住时间&#xff0c;点击右侧的日历就能自动得到离店时间&#xff0c;当时没有太留意是怎么实现的&#xff0c;现在在做项目时&…

【00】架构型

为什么80%的码农都做不了架构师&#xff1f;>>> 1、架构型&#xff08;archetype&#xff09; 一种形式&#xff0c;所有的东西或多或少地遵守。一种形式&#xff0c;属于同一类型的类都或多或少地遵守&#xff0c;包括属性、链接、方法、插入点、交互。 2、领域无…

SQL进阶提升(疑惑篇order by)-学习sql server2005 step by step(十一)

这篇主要发出两个疑惑&#xff0c;希望有兴趣的人解答&#xff0c;谢谢&#xff01; 1.newid()疑惑 1 create table tb (aa int,bb char(1)) 2 insert tb values(1,A) 3 insert tb values(1,B) 4 insert tb values(1,C) 5 insert tb values(1,D) 6 7 insert tb value…

钟南山团队在患者粪便中检出新冠活病毒,国家卫健委回应!专家:做好这事很重要...

全世界只有3.14 % 的人关注了青少年数学之旅2月13日下午&#xff0c;在广东省人民政府新闻办召开的疫情防控新闻发布会上&#xff0c;钟南山院士团队成员、广州医科大学国家呼吸疾病重点实验室副主任、教授赵金存介绍&#xff0c;该团队在P3实验室中&#xff0c;在中山大学附属…

CSDN《某一程序员竟然吃过shi?让我们走进他的生活,揭露背后的故事》

CSDN《某一程序员竟然吃过屎&#xff1f;我们走进他的生活&#xff0c;揭露背后的故事》 ——————————接下来让我们走进他的故事 到底是什么原因让他吃屎 这是这位程序员的自曝&#xff0c;我很好奇的不是他吃过屎&#xff0c;我在好奇是啥味的~ 接下来我们开始咨询这…

专升本c语言网课听谁的好_都说塑钢泥比玻璃胶好,填缝永不变黑,师傅却说不好用,听谁的?...

新房装修&#xff0c;我一点不想再用玻璃胶来填补缝隙了。像洗手台、淋浴房、厨房水槽这些地方&#xff0c;不管用多贵多好的玻璃胶&#xff0c;最后&#xff0c;它都会变黑发霉。朋友同我说&#xff0c;可以用塑钢泥替代&#xff0c;20年不发霉~他说&#xff0c;现在很多业主家…

技术分享|明源云天际集成开放平台接口中心基于IdentityServer4的鉴权机制

源宝导读&#xff1a;企业数字化生态建设中为解决集成多样性和资源统一管理的痛点引入企业级网关&#xff0c;网关作为资源访问的大门&#xff0c;身份认证鉴权是其业务的重中之重&#xff0c;本文将介绍企业级网关-天际集成开放平台是如何通过IdentityServer4来做到身份认证和…

设计模式--6大原则--单一职责原则

2019独角兽企业重金招聘Python工程师标准>>> 单一职责原则&#xff08;Single Responsibility Principle&#xff09;&#xff0c;简称SRP。 定义&#xff1a; There should never be more than one reason for a class to change. 应该有且仅有一个原因引起类的变更…

法国为何是伟大数学家的摇篮?

全世界只有3.14 % 的人关注了青少年数学之旅笛卡尔、韦达、帕斯卡、费马、拉格朗日、拉普拉斯、达朗贝尔、勒让德、蒙日、彭赛列、柯西、傅里叶、庞加莱、伽罗华、格罗藤迪克…… 这些令无数大学生“闻风丧胆”的数学家&#xff0c;基本上都诞生于十七至二十世纪的法国。解析几…

(转)WCF教程系列(1)-创建第一个WCF程序

作为微软技术.net 3.5的三大核心技术之一的WCF虽然没有WPF美丽的外观但是它却是我们开发分布式程序的利器但是目前关于WCF方面的资料相当稀少希望我的这一系列文章可以帮助大家尽快入门下面先介绍一下我的开发环境吧操作系统&#xff1a;windows vista business版本编译器&…

03 Files

本章提要-----------------------------------------------组成 oracle 的 8 种主要文件(包括 instance 和 database)instance: parameter file, trace file, alert filedatabase: data file, temp file, control file, redo log file, password file简单概述:parameter file: …