从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 分钟 · 慢步道人

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 分钟 · 慢步道人

使用海康威视SDK的那些坑

从其它平台迁移而来 由于工作需要,项目中有使用到海康威视的产品,不可避免的就要使用海康的SDK进行二次开发。开发过程中磕磕绊绊的,踩了不少坑,这里做一个简单的记录,算是给健忘的自己提个醒吧。 Delphi版本的接口 Gitee地址 首先,自己一直使用Delphi进行开发,然而海康官方只提供了C/C++的接口和示例,无奈只能自己改写了。改写完的部分已经上传,希望能有人共同来完善。 由于Delphi商业使用的限制,现已转到Lazarus,全面拥抱开源。 坑 播放声音 预览时播放声音,回放时播放声音,甚至使用播放库播放已下载的视频时播放声音,这些对于前端摄像头自带麦克的场景肯定是刚需(另接麦克的情况暂未测试),然而按照官方SDK文档和示例代码写出的程序死活就是没有声音,这样的情况似乎不少人都遇到过,但是,好像并没有见谁把解决方法公开过。 其实,这个问题特别简单,只需要把HCNetSDKCom目录下的OpenAL32.dll拷贝到PlayCtrl.dll所在的目录下就可以了。这下就明白了吧,没有声音的原因其实就是使用NET_DVR_OpenSound调了PlayCtrl.dll,而PlayCtrl.dll又调了OpenAL32.dll来播放声音,但是由于PlayCtrl.dll没有找到OpenAL32.dll所以没有声音,而且这个有问题的返回值也并没有一层层的返回给NET_DVR_OpenSound函数,结果就是函数返回调用成功了,但就是死活没声音。 PlayCtrl.dll不是PlayCtrl.dll 使用海康SDK进行二次开发的,一般也会使用到海康的播放库,但是有一个问题是需要注意的,那就是SDK里的PlayCtrl.dll并不是播放库里的PlayCtrl.dll。虽然它们长得一样,名字也一样,但它们的本质却是完全不一样的,是不能互相替代的!有兴趣的朋友可以使用eXeScope详细查看。

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

Delphi自定义图形控件的自定义字体属性在设计期报错的解决办法

从其它平台迁移而来 背景 自定义一个图形控件(继承自TGraphicControl类),需要在不同区域显示不同字体的内容,此时会需要在设计器中加入多个字体,方法是在控件的published区增加对应的字体属性即可(使用Ctrl+Shift+C可快速生成),如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 TMyGraphicControl = class(GraphicControl) private FText1Font: TFont; FText2Font: TFont; procedure SetText1Font(const Value: TFont); procedure SetText2Font(const Value: TFont); protected procedure Paint; override; public { public declarations } published property Text1Font:TFont read FText1Font write SetText1Font; property Text2Font:TFont read FText2Font write SetText2Font; end; 这样就可以在设计器里像使用原生控件一样使用自己的控件了。 问题 但是,如果在设计期选择了弹出字体对话框进行设置字体,IDE就会报错(大意是读或写某个地址异常),而在运行期则正常! 原因 对比查看Delphi自带的控件源码,终于找到了原因。 1 2 3 4 5 6 7 8 9 10 //Delphi TControl类设置字体属性的方法 procedure TControl....

2018-10-02 02:40:14 · 1 分钟 · 慢步道人