从Delphi到Go——字典

从其它平台迁移而来 字典,又称为哈希表,是一种能够快速寻找值的理想结构。Go语言中对应的数据类型是map,Delphi中是TDictionary泛型类。 声明 Delphi 1 2 uses System.Generics.Collections; var 字典名: TDictionary<键类型, 值类型>; Go 1 var 字典名 map[键类型]值类型 初始化 Delphi 1 字典名 := TDictionary<键类型, 值类型>.Create(初始容量); Go 1 2 3 4 //使用make构造 字典名 = make(map[键类型]值类型, 初始容量) //直接赋初值 字典名 = map[键类型]值类型{键1: 值1, 键2: 值2} 元素操作 Delphi 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var m: TDictionary<Integer, string>; //构造 m := TDictionary<Integer, string>....

2019-10-28 22:09:02 · 2 分钟 · 慢步道人

从Delphi到Go——数组

从其它平台迁移而来 静态数组 一维数组 声明 Delphi 1 var 数组名 : array[索引范围] of 元素类型; //索引范围是子界类型,格式为:下限..上限 Go 1 var 数组名 [数组长度]元素类型 Delphi的索引范围可以是任意的子界类型,而且是包含上下限的闭区间。子界可以是任意的序数类型(整型、字符型、枚举元素等),例如:0..8、5..11、'a'..'z'等。子界元素就是数组元素的下标。 Go的数组长度只能是整型,下标为0~数组长度-1。 初始化 Delphi 1 2 var 数组名 : array[1..N] of 元素类型 = (元素1, 元素2, ……, 元素N); //如果先声明后赋值的话,赋值时就需要遍历数组对每个元素分别赋值 Go 1 2 3 4 5 6 7 8 9 10 11 var 数组名 [N]元素类型 = [N]元素类型{元素0, 元素1, ……, 元素N-1} //由于初始化时元素个数已知,以上代码也可写为: var 数组名 [N]元素类型 = [...]元素类型{元素0, 元素1, ……, 元素N-1} //如果先声明后赋值的话,写法如下: var 数组名 [N]元素类型 数组名 = [....

2019-10-08 22:03:53 · 3 分钟 · 慢步道人

再探Delphi字符串

从其它平台迁移而来 闲来无事,又开始扒拉起Delphi的源码,这次发现一个比较有意思的函数StringCodePage,作用是返回传入字符串的CodePage。至于什么是CodePage,暂且认为是字符编码吧。 先测试一把: 1 2 3 4 5 6 7 8 9 10 11 12 13 var s1: AnsiString; s2: WideString; s3: UTF8String; cp1, cp2, cp3: Word; begin s1 := '123abc中国'; s2 := '123abc中国'; s3 := '123abc中国'; cp1 := StringCodePage(s1); //936 - GBK(简体中文) cp2 := StringCodePage(s2); //1200 - UCS-2LE Unicode 小端序 cp3 := StringCodePage(s3); //65001 - UTF-8 Unicode end; 来看下是怎么实现的: 1 2 3 4 5 6 7 function StringCodePage(const S: UnicodeString): Word; overload; begin if S <> '' then Result := PWord(PByte(S) - 12)^ // StrRec....

2019-10-06 04:43:45 · 4 分钟 · 慢步道人

从Delphi到Go——基础

从其它平台迁移而来 废话 长期从事Delphi开发,虽不敢说精通,但说很熟悉还是相当有自信的。不过,只会一门语言,而且还是这么老的语言,更是在大天朝很小众的语言,总感觉自己离饿死街头没多远了,所以趁着还没老再学个潮点的吧。 先前考虑过Python,初步了解后觉得不太适合自己: 解释型语言:部署时得先搞个运行环境,发布的程序就是源码本身,再加上这个执行效率,怎么想都还是编译型语言更合适。 动态语言:无需声明,拿来就用,这已经很不合习惯了。想想一个变量,前一秒还是浮点数,下一秒就成字符串了,再一眨眼又成某个对象了……虽然一般不会有人这么写,但是挡不住手误啊,还是把这种小细节交给编译器更让人放心。 所以,对于有点强迫症和洁癖的自己,最后还是选了Go,比较符合已有的编程习惯,学习成本应该相对会低些吧。 至于Go嘛,想学是已经很久了,但由于种种原因却迟迟未开启,不过终究还是要迈出这一步的,所以就搞这么个系列来记录吧,一方面算是自我督促,另一方面也算是一种交流吧,当然,若一不留神帮上了谁,那自是开心极了。 言归正传 已经初步了解过了Go,说来和Delphi还是有不少相似之处呢,从Delphi转向Go应该会比较轻松吧。 工程结构 Delphi的工程算是比较自由的,源码的话,只要把单元路径引了或是直接包含进工程单元里就可以了,编译出的dcu和最终的exe指定下路径也就没问题了,通常我都使用下面这种结构: 1 2 3 4 5 6 7 8 9 10 11 Project/ bin/ src/ dcu/ mod1/ *.dfm *.pas mod2/ *.dfm *.pas *.dpr 不过,每一个工程都要设置,而且我习惯将Debug和Release设置完全一样,也还真是够烦的。 Go就没得选了,只有一种结构: 1 2 3 4 5 6 7 8 9 10 11 Project/ bin/ pkg/ src/ *.go mod1/ *.go *_test.go mod2/ *.go *_test.go 整体和我原有的习惯差不多,还是蛮容易接受的,不过倒是要把这Project的路径加入到GOPATH系统变量里让人有一点小不爽。但是Go可以直接把测试都写了,这点还是蛮让我惊喜的,毕竟用了这么多年Delphi也没写过一行像样的测试。 源码结构 Delphi典型的源码结构是这样: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 unit Unit1; interface uses ....

2019-09-09 23:30:04 · 7 分钟 · 慢步道人

关于TField.DataSize的坑

从其它平台迁移而来 在从数据库中查询数据时,有时需要事先取得字段内容的大小,再根据情况进行处理。 对于ADO之类返回TField类型的,可以使用DataSize属性,但是!!!这里有很深的坑!!!。 首先看如下代码: 1 2 3 4 if ADOQuery.FieldByName('Test').DataSize > 3 then {处理1} else {处理2}; 按预想,当Test字段里的数据超过3B时,应该执行处理1的代码,但事实上无论该内容长短,都是执行处理2的代码,WHY? 扒一下Delphi的源码就明白了。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 function TField....

2019-07-11 20:03:00 · 3 分钟 · 慢步道人

Delphi安全结束线程

从其它平台迁移而来 在开发过程中,不可避免的要用到多线程,而线程的同步、释放等又可能引入新的问题,不过网上已有许多资料,这里重点说下我使用的方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 type TMyThread = class(TThread) protected procedure Execute; override; public constructor Create(...); destructor Destroy; override; //使用 reintroduce 关键字可以明确通知编译器屏蔽父类的同名方法而使用自己的方法。 procedure Free; reintroduce; end; constructor TMyThread.Create(...); begin { 在这里创建相关对象,可以省去先挂起线程再恢复的操作 } inherited Create; end; destructor TMyThread....

2019-04-28 19:13:48 · 1 分钟 · 慢步道人

关于窗口置屏的那个坑

从其它平台迁移而来 在开发多屏应用程序的时候,经常需要把某个窗口置到某个屏上的某个位置。以下是一个Delphi写的置屏方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 procedure ShowInMonitor(Sender: TObject; AIndex: Integer; ALeft: Integer = 0; ATop: Integer = 0); var lM: TMonitor; begin if Sender is TControl then begin if AIndex > Screen.MonitorCount - 1 then begin AIndex := 0; end; lM := Screen.Monitors[AIndex]; (Sender as TControl).Left := lM.Left + ALeft; (Sender as TControl).Top := lM.Top + ATop; end; end; Sender是需要置屏的窗口;AIndex是置屏的目标屏号,从0开始;ALeft是水平偏移量,ATop是垂直偏移量,默认均为0,即在目标屏的左上角。...

2019-03-01 16:16:46 · 1 分钟 · 慢步道人

DBGridEh显示、编辑标记字段

从其它平台迁移而来 在用Delphi开发数据库应用过程中,经常需要用到DBBrid来显示或编辑数据库表,但相对于IDE自带的DBBrid,DBGridEh显然要更好用一些(cxDBGrid也同样好使,这里只对DBGridEh进行说明)。 在数据库表结构设计时,通常会习惯使用整型字段来存储如男、女、已修改、已删除、已作废等具有标记性的信息,好处是占存储空间小且可扩展性强,缺点是显示方式不友好,但是,使用DBGridEh的一些设置可以弥补这个缺陷。 假设数据库里的表T_Demo里有一个表示性别的字段Sex,其用0表示男,1表示女,2表示未知,用DBGridEh显示时仍为0、1、2,十分不友好。这时只要做一点美化即可。 在DBGridEh的对应列的KeyList属性里分别加入0、1、2三个数字,每个数字一行,用代码实现的话就是: 1 2 3 4 5 //这里假设第一列就是性别,以下代码相同 DBGridEh.Columns[0].KeyList.Clear; DBGridEh.Columns[0].KeyList.Append('0'); DBGridEh.Columns[0].KeyList.Append('1'); DBGridEh.Columns[0].KeyList.Append('2'); 在DBGridEh的对应列的PickList属性里分别加入男、女、未知三行,用代码实现是: 1 2 3 4 DBGridEh.Columns[0].PickList.Clear; DBGridEh.Columns[0].PickList.Append('男'); DBGridEh.Columns[0].PickList.Append('女'); DBGridEh.Columns[0].PickList.Append('未知'); 这样,原先显示为0、1、2的性别就变成了男、女、未知。也可以加上图片来进一步美化。 放置一个TImagList控件,并命名为ilSex,然后分别添加三个图片,比如像卫生间的男、女图片和问号,注意添加后图片对应的索引,否则会张冠李戴。 在DBGridEh的ImagList属性里关联ilSex,并且设置ShowImageAndText属性为True,用代码是: 1 2 DBGridEh.ImagList := ilSex; DBGridEh.ShowImageAndText := True; 这样,性别这一列就有图片有文字,比0、1、2要友好很多,而且在编辑的时候也可以直接下拉进行选择来实现性别的录入。

2019-01-25 11:37:40 · 1 分钟 · 慢步道人

生成指定范围和个数的不重复的随机整数

从其它平台迁移而来 生成指定范围内的随机数,有相应的随机函数(如RandomRange(x, y)可生成x <= d < y的随机整数),或者在基本的随机函数上稍加修改也可生成;生成 n 个随机数,只需调用 n 次随机函数即可;生成 n 个不重复的随机数,就会有一点点小麻烦。 常规思路 一般来说,要生成n个不重复的随机数,只需判断每次生成的随机数有没有和这前生成的随机数重复即可,若重复即抛弃,不重复则记录。 但是,这样要进行很多额外的判断,而且当生成的量变大时,这样的判断次数也几乎是呈指数级的增加(具体复杂度没有进行详细分析)。 另一种思路 如果每生成一个随机整数,就在一个整数序列上对应的位置做一个标记,那么只需要判断标记的个数有没有达到n即可,然后把有标记的整数取出就是 n 个不重复的随机整数。(其实该思路是借鉴了某个排序算法的思路,具体算法名称不记得了) 首先来考虑生成 n 个[0, m)的不重复的随机数方法,n < m。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var i: Integer; tmpMark: array of Integer; begin SetLength(tmpMark, m); repeat Randomize; i := RandomRange(0, m); //[0,m)半开半闭区间 tmpMark[i] := 1; until SumInt(tmpMark) = n; for i := 0 to m - 1 do if tmpMark[i] = 1 then i; //i即为随机出的不重复的整数 end; 本例中借助长度为m的数组tmpMark来进行标记,同时使用delphi自带的SumInt(在Math单元)函数来计算标记的个数,有标记的tmpMark下标即为随机出的整数。...

2019-01-14 10:48:06 · 1 分钟 · 慢步道人

TClientDataSet的使用以及遇到的坑

从其它平台迁移而来 在Delphi未加入FireDAC之前,似乎是没有内存表控件的(也许有,可能我不知道吧),但是可以用TClientDataSet控件来做内存表使用,即使有了FireDAC可以使用TFDMemTable,我还是觉得TClientDataSet更好用一些。 做内存表使用 创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 with ClientDataSet do begin Close; //定义字段 with FieldDefs do begin Clear; Add('Field1', ftInteger, 0, False); Add('Field2', ftString, 0, False); ...... end; //创建结构 CreateDataSet; Open; end; 排序 在定义字段后,创建结构前,也可以指定排序字段。 1 IndexFieldNames := 'Field1'; 做缓存使用 需要结合TDataSetProvider来使用。以下示例以使用ADO组件为例。 拉取数据 方法1 TDBGrid->TDataSource->TClientDataSet->TDataSetProvider->TADOQuery->TADOConnection 1 2 3 4 5 6 7 ClientDataSet.ProviderName := DataSetProvider.Name; //设计器里设置过就不需要了 with ClientDataSet do begin Close; CommandText:='select * from T_Table'; Open; end; 方法2 TDBGrid->TDataSource...

2019-01-12 14:43:15 · 1 分钟 · 慢步道人