499人参与 • 2024-05-16 • Delphi
quicklib是一个快速开发库,它提供了诸如automapper、linq、ioc依赖注入、memorycache、计划任务、json和yml配置、序列化程序等多种功能。这个库特别支持delphi和firemonkey的多平台开发,包括windows、linux、android、osx和ios。同时,quicklib也支持freepascal,使得开发人员能够更轻松地构建跨平台应用程序并提高生产力。
功能领域:
主要单元描述:
允许控制台应用程序以控制台模式或服务模式运行相同的代码,从而简化调试任务。
if not appservice.isrunningasservice then
begin
...你的代码以控制台模式运行
end
else
begin
appservice.servicename := 'myservice';
appservice.displayname := 'myservicesvc';
//你可以将匿名方法传递给事件
appservice.onstart := procedure
begin
...你的启动代码
end;
appservice.onexecute := yourexecutefunction;
appservice.onstop := yourstopfunction;
appservice.checkparams;
end;
简化了与azure和amazon云存储的blob交互。
//连接到azure blob存储
quickazure := tquickazure.create(azureaccountname, azureaccountkey);
//将blob文件下载到流中
done := quickazure.getblob('mycontainer', 'myfile.jpg', responseinfo, mystream);
//检查是否存在文件夹
found := existfolder('mycontainer', '/public/documents/personal');
//列出以特定模式开头的blobs(递归或非递归)
for azblob in listblobs('mycontainer', '/public/documents', recursive, responseinfo) do
begin
if azblob.size > 1000 then showmessage(azblob.name);
end;
提供cidr和ip范围功能。
//将ip字符串转换为整数
ipv4toint('192.168.1.10');
//获取子网范围的第一个和最后一个ip
getiprange('192.168.100.0', '255.255.255.0', lowip, highip);
开发人员日常工作中经常需要的函数。
//将utc时间tdatetime转换为本地日期时间
utctolocaltime(myutctime);
//生成一个长度为10的随机密码,包含字母数字和符号。
randompassword(10, [pfincludenumbers, pfincludesigns]);
//将短语中的每个单词首字母大写
capitalizeall('the grey fox'); //返回 "the grey fox"
//简单的tcounter和ttimecounter用于循环
counter := tcounter;
counter.init(200);
timecounter : ttimecounter;
timecounter.init(10000);
while true do
begin
inc(n);
{你的过程处理代码在这里}
//每200步写入控制台
if counter.check then writeln(format('processed %d entries', [n]));
//每10秒写入控制台
if timecounter.check then writeln('im working...');
end;
计时器和代码基准测试很简单。
//获取代码部分执行所消耗的时间
chrono := tchronometer.create(false);
chrono.start;
...你需要基准测试的代码
chrono.stop;
//以长时间格式显示经过的时间(例如2小时10分钟)
showmessage(chrono.timeelapsed(true));
//以短时间格式显示经过的时间(例如02:10:00)
showmessage(chrono.timeelapsed(false));
//获取进程的基准测试信息
chrono := tchronobenchmark.create;
chrono.totalprocess := 100000;
for i := 1 to 10000 do
begin
{你的进程代码在这里}
chrono.currentprocess := i;
//显示你的进程预计需要的时间,格式为x小时x分钟x秒
writeln(chrono.estimatedtime(true));
//显示你的进程处理速度:每秒处理的项数
writeln(format('items processed %d/sec', [chrono.speed]));
end;
writeln(chrono.elapsedtime(false)); //以00:00:00格式显示总经过时间
将日志消息以不同颜色等写入控制台。
//定义需要的输出级别
console.verbose := log_debug;
//以红色在控制台上写行
cout('error x', eterror);
//以绿色格式化输出行
coutfmt('proccess %s finished', [proccesname], etsuccess);
//写整数
cout(12348);
//连接quicklog并一行代码同时写入磁盘和屏幕(具有独立的详细级别)
myquicklog := tquicklog.create;
myquicklog.verbose := log_all;
console.verbose := log_onlyerrors;
console.log := myquicklog;
记录到磁盘或内存,具有详细的日志等级和每日或最大空间轮换功能。
// 在开始时写入包含运行路径、应用程序名称、调试模式、用户等信息的页眉
log.showheader := true;// 设置20mb时轮换的日志
log.setlog('.\mylog.log',false,20);// 写入一条错误信息
log.add('error x',eterror);// 写入格式化的错误信息
log.add('error is %s',[errorstr],eterror);quick.config:
以json、yaml文件或windows注册表项的形式加载/保存配置。从tappconfigjson、tappconfigyaml或tappconfigregistry创建一个派生类,并添加将加载/保存的已发布的属性。当检测到文件更改时,可以重新加载文件配置。// 创建一个类继承
tmyconfig = class(tappconfigjson)
private
fname : string;
fsurname : string;
fstatus : integer;
published
property name : string read fname write fname;
property surname : string read fsurname write fsurname;
property status : integer read fstatus write fstatus;
end;// 将配置创建为json文件
// 在您的uses中添加quick.config.json
myconfig := tmyconfig.create('config.json');
myconfig.provider.createifnotexists := true;
myconfig.provider.reloadiffilemodified := true;
myconfig.name := 'john';
myconfig.surname := 'smith';
// 加载
myconfig.load;
// 保存
myconfig.save;// 将配置创建到windows注册表中
// 在您的uses中添加quick.config.registry
myconfig := tmyconfig.create;
// 将注册表定义为hkey_current_user\software\myapp
myconfig.hroot := hkey_current_user;
myconfig.mainkey := 'myapp';
myconfig.name := 'john';
myconfig.surname := 'smith';
// 加载
myconfig.load;
// 保存
myconfig.save;// 创建一个没有默认提供者的自定义配置
tmyconfig = class(tappconfig)
... 您的属性
end;myconfig := tmyconfig.create(tappconfigjsonprovider.create('.\config.json');
监视文件的更改并触发事件。
filemonitor.filename := '.\myfile.txt'; // 每2秒检查一次文件更改 filemonitor.interval := 2000; // 监视文件被删除或修改的事件 filemonitor.notifies := [mnfilemodified, mnfiledeleted)]; filemonitor.onfilechange := myfilechangefunction; filemonitor.enabled := true;
用于处理json对象的工具。
// 当在uses中声明单元时,tobject helper允许将所有对象从/加载到json字符串 myobject.fromjson := jsonstring; mystring := myobject.tojson;// 您可以使用clone函数克隆简单对象 myobject1.clone(myobject2);
用两行代码发送电子邮件。
// 发送电子邮件
smtp := tsmtp.create('mail.domain.com',25,false);
smtp.sendmail('my@email.com','to@email.com','email subject','my message body');// 您可以定义更高级的选项
smtp.sendername := 'john';
smtp.from := 'my@email.com';
smtp.recipient := 'one@email.com,two@email.com';
smtp.subject := 'email subject';
smtp.addbodyfromfile := '.\body.html';
smtp.cc := 'other@email.com';
smtp.bcc := 'more@email.com';
smtp.attachments.add('.\notes.txt');
smtp.sendmail;
线程安全类。
tthreadedqueuecs: 使用临界区的tthreadedqueue版本。
tthreadobjectlist: 线程安全的对象列表。
tthreadedqueuelist: 线程安全的队列列表。支持自动增长和临界区。
tanonymousthread: 创建匿名线程,定义非链式的execute和onterminate方法。如果代码需要更新ui,请使用execute_sync和onterminate_sync方法。
// 简单的匿名线程
tanonymousthread.execute(
procedure
var
i : integer;
begin
for i := 0 to 10 do cout('working %d',[i],ettrace);
cout('executed thread',etsuccess);
end)
.onterminate(
procedure
begin
cout('terminated thread',etsuccess);
cout('press <enter> to exit',etinfo);
end)
.start;
truntask: 启动一个自动释放的单任务线程,具有故障和重试控制策略。可以在代码中传递和创建参数。
truntask.execute(
procedure(task : itask)
var
stream : tstringstream;
response : ihttprequestresponse;
begin
stream := tstringstream.create;
try
response := tjsonhttpclient(task['httpclient'].asobject).get(task['url']);
task.result := response.statuscode;
if response.statuscode <> 200 then raise exception.create(response.statustext);
finally
stream.free;
end;
end)
.setparameter('httpclient',(tjsonhttpclient.create),true)
.setparameter('url','https://mydomain.com/testfile')
.waitandretry(5,250,2)
.onretry(
procedure(task : itask; aexception : exception; var vstopretries : boolean)
begin
//if error 404 don't try to retry request
if task.result = 404 then vstopretries := true;
end)
.onexception(
procedure(task : itask; aexception : exception)
begin
coutfmt('exception downloading (error: %s / statuscode: %d)...',[aexception.message,task.result.asinteger],eterror);
end)
.onterminated(
procedure(task : itask)
begin
if task.done then coutfmt('download "%s" finished ok',[task['url'].asstring],etsuccess)
else coutfmt('download "%s" failed after %d retries',[task['url'].asstring,task.numretries],eterror);
end)
.run;
tbackgroundstasks: 在后台启动任务,允许一定数量的并发工作线程,并具有故障和重试控制策略。如果代码需要更新ui,请使用addtask_sync和onterminate_sync方法。
<name>]或task.[index]访问。 backgroundtasks := tbackgroundtasks.create(10);
for i := 1 to 100 do
begin
mytask := tmytask.create;
mytask.id := i;
mytask.name := 'task' + i.tostring;
backgroundtasks.addtask([mytask],false,
procedure(task : itask)
begin
cout('task %d started',[tmytask(task.param[0].asobject).id],etdebug);
tmytask(task.param[0].asobject).dojob;
end
).waitandretry([250,2000,10000])
).onexception(
procedure(task : itask; aexception : exception)
begin
cout('task %d failed (%s)',[tmytask(task.param[0].asobject).id,aexception.message],eterror);
end
).onterminated(
procedure(task : itask)
begin
cout('task %d finished',[tmytask(task.param[0].asobject).id],etdebug);
tmytask(task.param[0].asobject).free;
end
).run;
end;
backgroundtasks.start;
tscheduledtasks: 定时器的替代方案。您可以分配具有开始时间、重复选项、到期日期和故障及重试控制策略的任务。如果代码需要更新ui,请使用addtask_sync、onterminate_sync和onexpired_sync方法。
您可以为执行、异常、终止和到期事件分配匿名方法。
myjob := tmyjob.create;
myjob.name := format('run at %s and repeat every 1 second until %s',[datetimetostr(scheduleddate),datetimetostr(expirationdate)]);
scheduledtasks.addtask('task1',[myjob],true,
procedure(task : itask)
begin
cout('task "%s" started',[tmytask(task.param[0]).name],etdebug);
tmyjob(task.param[0]).dojob;
end
).onexception(
procedure(task : itask; aexception : exception)
begin
cout('task "%s" failed (%s)',[tmyjob(task.param[0]).name,aexception.message],eterror);
end
).onterminated(
procedure(task : itask)
begin
cout('task "%s" finished',[tmyjob(task.param[0]).name],etdebug);
end
).onexpired(
procedure(task : itask)
begin
cout('task "%s" expired',[tmyjob(task.param[0]).name],etwarning);
end
).startat(scheduleddate
).repeatevery(1,ttimemeasure.tmseconds,expirationdate);
scheduledtasks.start;
itask: 传递给truntask、tbackgroundtasks和tscheduledtasks的每个任务事件的接口。
管理失败和重试策略,定义最大重试次数、重试之间的等待时间和熔断机制。
管理windows进程。
// 终止explorer进程
killprocess('explorer.exe');
// 判断一个应用程序是否正在运行
if isprocessrunning('explorer.exe') then showmessage('explorer正在运行!');
// 获取运行exe的用户名
writeln('explorer.exe由以下用户打开:' + getprocessuser('explorer.exe'));
// 使用20秒的超时获取窗口句柄
if findwindowtimeout('mainwindow',20) then writeln('检测到窗口');
管理windows服务。
// 检测服务是否已安装
if not serviceispresent('localhost','mysvc') then raise exception.create('服务未安装!');
// 启动服务
servicestart('localhost','mysvc');
// 卸载服务
serviceuninstall('mysvc');
字符串格式化。
// 将字节格式化为mb、gb、tb... formatbytes(50000) // 显示 50kb formatbytes(90000000) // 显示 90mb
将对象序列化为json文本或从json文本反序列化为对象。您可以定义是否处理公开或已发布的属性(仅delphi,fpc rtti仅支持已发布的属性)。
json := '{"name":"peter","age":30}';
serializer := tjsonserializer.create(tserializelevel.slpublishedproperty);
try
serializer.jsontoobject(user,json);
finally
serializer.free;
end;
将一个类的字段映射到另一个类。允许自定义映射以匹配不同的字段,并允许自定义映射过程以手动转换字段。
// 将user1的值映射到user2
tmapper<tuser2>.map(user);
// 自定义映射
automapper := tautomapper<tuser,tuser2>.create;
// 选项1:您可以定义自动映射不同名称的属性
automapper.custommapping.addmap('cash','money');
automapper.custommapping.addmap('id','iduser');
// 选项2:您可以决定手动修改每个属性或允许自动映射某些属性
automapper.ondomapping := procedure(const asrcobj : tuser; const atargetname : string; out value : tflexvalue)
begin
if atargetname = 'money' then value := asrcobj.cash * 2
else if atargetname = 'iduser' then value := asrcobj.id;
end;
// 选项3:您可以在自动映射完成后修改某些属性
automapper.onaftermapping := procedure(const asrcobj : tuser; atgtobj : tuser2)
begin
atgtobj.money := asrcobj.cash * 2;
atgtobj.iduser := asrcobj.id;
end;
user2 := automapper.map(user);
用作dto类,包含json序列化和映射功能。
type
tuser = class(tjsonrecord)
private
fname : string;
fage : integer;
published
property name : string read fname write fname;
property age : integer read fage write fage;
end;
var
user, user2 : tuser;
begin
user := tuser.create;
// 展示为json字符串
writeln(user.tojson);
// 映射到其他类
user.mapto(user2);
writeln(user2.tojson);
// 从文件加载
user.loadfromfile('.\user.json');
// 保存到文件
user2.savetofile('.\user2.json');
end;
带有索引或搜索功能的改进列表。
var
users : tindexedobjectlist<tuser>;
begin
users := tindexedobjectlist<tuser>.create(true);
// 根据属性"name"创建索引
users.indexes.add('name','name',tclassfield.cfproperty);
// 根据私有字段"id"创建索引
users.indexes.add('id','fid',tclassfield.cffield);
// 通过"name"索引获取用户
writeln(users.get('name','peter').surname);
end;
flexvalue可以存储任何数据类型,并允许使用集成操作符和自动释放功能传递给其他类。
var value : tflexvalue; str : string; num : integer; begin value := 'hello'; str := value; value := 123; num := value; end;
改进的数组。
txarray: 带有类似tlist方法的数组。
(注意:下面的代码段似乎被错误地复制了 tindexedobjectlist的示例,这里应该展示 txarray的使用。)
var users : txarray<tuser>; begin users := txarray<tuser>.create; users.add(user); // 假设user已经是一个tuser类型的对象 // ... 其他txarray的操作 end;
tflexarray: 可以存储不同值类型的数组,类似于tlist。
var
flexarray : tflexarray;
begin
flexarray.add(10);
flexarray.add('hello');
user := tuser.create;
try
user.name := 'joe';
flexarray.add(user);
cout('integer item = %d',[flexarray[0].asinteger],etinfo);
cout('string item = %s',[flexarray[1].asstring],etinfo);
cout('record item = %s',[tuser(flexarray[2]).name],etinfo);
finally
user.free;
end;
end;
tflexpairarray: 可以存储不同值类型的数组,并可以通过项目名称进行搜索,类似于tlist。
var
flexarray : tflexpairarray;
begin
flexarray.add('onenumber',10);
flexarray.add('other','hello boy!');
user := tuser.create;
try
user.name := 'joe';
flexarray.add('myuser',user);
cout('integer item = %d',[flexarray.getvalue('onenumber').asinteger],etinfo);
cout('string item = %s',[flexarray.getvalue('other').asstring],etinfo);
cout('record item = %s',[tuser(flexarray.getvalue('myuser')).name],etinfo);
finally
user.free;
end;
end;
yaml对象结构。
tyamlobject: yaml对象是一个yaml值对的数组。
// 从yaml文本创建yaml对象
yamlobj.parseyamlvalue(ayaml);
// 添加一个对
yamlobj.addpair('name','mike');
// 显示为yaml结构
writeln(yamlobj.toyaml);
tyamlarray: 对象或标量的数组。
yamlarray.addelement(tyamlpair.create('age',30));
yamlobj.addpair('myarray',yamlarray);
tyamlpair: 名称-值对。值可以是对象、数组或标量。
n := yamlobj.getpair('name').value as tyamlinteger;
将对象序列化/反序列化为yaml。
// 序列化 text := yamlserializer.objecttoyaml(obj); // 反序列化 yamlserializer.yamltoobject(obj, yamltext);
使用表达式评估对象属性或单个值。
if texpressionparser.validate(user, '(age > 30) and (dept.name = "financial")') then begin // 执行一些操作 end; if texpressionparser.validate(user, '(20 > 30) or (5 > 3)') then begin // 执行一些操作 end;
对任何 tobjectlist<t>、tlist<t>、tarray<t>和 txarray<t>执行linq查询,通过类似sql语法的复杂where子句进行select、更新和排序列表。where子句使用命名空间来确定嵌套属性。linq可以在属性数组中搜索元素。
现在包括一个 tarray<string>助手,用于在数组中添加、删除和通过正则表达式搜索。
// 多条件选择
for user in tlinq<tuser>.from(userslist).where('(name = ?) or (surname = ?) or (surname = ?)', ['peter', 'smith', 'huan']).select do
begin
// 执行一些操作
end;
// 选择并更新字段
tlinq<tuser>.from(userlist).where('surname like ?', ['%son']).selectfirst.name := 'robert';
// 选择顶部并按字段排序
for user in tlinq<tuser>.from(userlist).where('age > ?', [18]).selecttop(10).orderby('name') do
begin
// 执行一些操作
end;
// 按条件更新字段
tlinq<tuser>.from(userlist).where('name = ?', ['peter']).update(['name'], ['joe']);
// 计数结果
numusers := tlinq<tuser>.from(userlist).where('(age > ?) and (age < ?)', [30, 40]).count;
tcustomhttpserver是一个简单的接口httpserver,具有自己的httprequest和httpresponse实现,允许轻松更改httpserver引擎。
您可以启用自定义错误页面以返回自定义页面和动态错误页面。
thttpserver是indyhttpserver的实现,但您可以定义自己的实现。
tmyhttpserver = class(thttpserver) public procedure processrequest(arequest: ihttprequest; aresponse: ihttpresponse); override; end; procedure tmyhttpserver.processrequest(arequest: ihttprequest; aresponse: ihttpresponse); begin aresponse.contenttext := 'hello world!'; end;
使用过期时间缓存对象或字符串,以避免每次需要时都生成这些信息(数据库查询、难以计算的信息等)。tmemorycache允许缓存对象和字符串。泛型版本tmemorycache <t>仅允许缓存定义的类型。
// 创建具有10秒清除间隔的memorycache cache := tmemorycache.create(10); // 为特定类型创建memorycache cache := tmemorycache<tmyobj>.create;
// 将字符串设置到缓存中,没有过期时间
cache.setvalue('mystring', 'hello world');
// 将字符串设置到缓存中,10秒后过期
cache.setvalue('mystring', '这个缓存将在10秒后过期');
// 将对象设置到缓存中
cache.setvalue('obj1', valueobj);
// 获取字符串查询结果
cache.getvalue('query12');
// 获取整数
cache.trygetvalue<integer>('number', valueint);
// 获取对象
cache.trygetvalue('obj1', valueobj);
// 创建具有20秒清除间隔并使用lzo引擎压缩的memorycache cache := tmemorycache.create(10, nil, tcachecompressorlzo.create);
控制反转管理器允许自动创建接口或实例化对象,或在构造函数类中自动注入它们,以避免依赖关系。
创建一个容器来管理依赖注入。
ioccontainer := tioccontainer.create;
注册类型:
在注入之前,您需要注册类型。类型可以作为singleton或transient注册。
singleton:生命周期将是所有注入的一个单一实例,类似于全局变量。
transient:生命周期将是每次注入的一个新实例。
将接口类型作为transient注册到容器中:
ioccontainer.registertype<imultservice, tmultservice>.astransient;
将接口类型作为singleton注册,并委托构造:
ioccontainer.registertype<isumservice, tsumservice>.assingleton.delegateto(
function : tsumservice
begin
result := tsumservice.create;
end
);
注册实例:
将命名实例对象作为transient注册,并委托构造:
ioccontainer.registerinstance<tdivideservice>('one').astransient.delegateto(
function : tdivideservice
begin
result := tdivideservice.create(true);
end
);
注册选项:
注册ioptions(仅适用于singleton):
ioccontainer.registeroptions<tmyoptions>(myoptions);
解析类型:
abstractfactory:
尝试使用依赖注入解析所有创建方法参数来创建类。
myclass := ioccontainer.abstractfactory<tmybaseclass>(tmyclass);
解析接口依赖:
multservice := ioccontainer.resolve<imultservice>; result := multservice.mult(2, 4);
解析实例:
解析命名实例依赖:
divideservice := ioccontainer.resolve<tdivideservice>('other');
result := divideservice.divide(100, 2);
接口实例将自动释放,但实例依赖项仅当定义为singleton时才会被释放,transient实例将由代码销毁。#3
您可以将部分定义为类,并将其保存为单个文件设置。其工作方式类似于dotnet options。选项文件可以是json或yaml格式。
定义从toptions继承的选项类,所有已发布的属性都将被加载/保存。
创建选项容器,使用jsonserializer并在更改时重新加载:
options := toptionscontainer.create('.\options.conf', tjsonoptionsserializer.create, true);
向容器选项添加一个部分:
options.addsection<tloggingoptions>('logging');
配置选项:
您可以定义要保存到文件中的部分名称,并委托配置默认设置和验证值:
options.addsection<tloggingoptions>('logging').configureoptions(
procedure(aoptions: tloggingoptions)
begin
aoptions.path := 'c:\';
end
).validateoptions;
验证选项:
验证选项允许验证选项设置是否在定义的范围内。此验证需要先在toptions类中的属性上分配自定义属性。
tloggingoptions = class(toptions)
private
fpath : string;
published
[required, stringlength(255, 'path too long')]
property path : string read fpath write fpath;
[range(0.0, 5.2)]
property level : double read flevel write flevel;
end;
使用选项:
检索选项部分:
loggingoptions := options.getsection<tloggingoptions>; loggingoptions.path := 'c:\path';
使用ioptions:
ioptions是一个可注入依赖的toptions接口。您可以使用ioccontainer.registeroptions <toptions>注册它以使其可注入到构造函数方法中。
uioptions := options.getsectioninterface<tuioptions>.value; uioptions.windowcolor := clblue;
加载/保存选项:
从文件设置中加载选项:
options.load;
将选项保存到文件设置:
options.save;
如果您在创建容器时定义了reloadonchanged参数为true,则每次文件设置更改时,配置都将重新加载。如果您需要控制何时重新加载,可以监听事件:
options.onfilemodified := procedure
begin
cout('detected config file modification!', etwarning);
end;
定义连接池、线程池或您想要控制的任何对象池,以避免资源消耗,如数据库连接、http客户端等。
创建http客户端池:
pool := tobjectpool<thttpclient>.create(5, 5000, procedure(var ainstance: thttpclient)
begin
ainstance := thttpclient.create;
ainstance.useragent := 'myagent';
end);
从池中获取对象:
httpcli := pool.get.item;
statuscode := httpcli.get('https://www.mydomain.com').statuscode;
定义了具有linq支持的接口list和objectlist。
<t> / ilist <t>:允许使用linq进行正则表达式搜索/删除/更新的接口list。myarray := ['joe', 'mat', 'lee'];
// 搜索正则表达式匹配项
cout('search for regex match', ccyellow);
for name in myarray.where('e$', true).select do
begin
cout('user %s ends with "e"', [name], etinfo);
end;
<t> / iobjectlist <t>:允许使用linq谓词或表达式进行搜索/删除/更新的接口objectlist。user := listobj.where('profile.name = ?', ['lee']).selectfirst;
对项数组的表达式搜索:
users := listobj.where('roles contains ?', ['superadmin']).select;
谓词搜索:
user := listobj.where(function(auser: tuser): boolean
begin
result := auser.name.startswith('j');
end).selectfirst;
if user <> nil then cout('%s starts with j letter', [user.name], etinfo);
查看quick.linq部分以查看更多允许的函数。
使用字典或委托函数进行字符串模板替换。您可以指定引号的标记字符。
通过传递字典进行替换:
dict := tdictionary<string, string>.create;
dict.add('user', 'john');
dict.add('age', '20');
dict.add('surname', 'peterson');
mytemplate := 'user {{user}} {{surname}} are {{age}} years old.';
template := tstringtemplate.create('{{', '}}', dict);
result := template.replace(mytemplate);
使用委托函数进行替换:
mytemplate := 'user {{user}} {{surname}} are {{age}} years old.';
template := tstringtemplate.create('{{', '}}', function(const atoken: string): string
begin
if atoken = 'user' then result := 'john'
else if atoken = 'surname' then result := 'peterson'
else if atoken = 'age' then result := '20';
end);
result := template.replace(mytemplate);
调试工具,用于检查性能并获取进入和退出方法的检查点。通过debug编译器指令定义,仅在应用程序以调试模式编译时激活。
在控制台应用程序中,默认使用控制台输出。您可以传递一个记录器以输出到:
tdebugutils.setlogger(ilogger);
跟踪代码的一部分:
function tcalculator.subs(a, b: int64): int64;
begin
{$ifdef debug}
tdebugger.trace(self, format('substract %d - %d', [a, b]));
{$endif}
result := a - b;
// 模拟工作200毫秒
sleep(200);
end;
// 返回:
// 29-06-2020 23:31:41.391 [trace] tcalculator -> substract 30 - 12
计算从点到退出函数的处理时间:
function tcalculator.sum(a, b: int64): int64;
begin
{$ifdef debug}
tdebugger.timeit(self, 'sum', format('sum %d + %d', [a, b]));
{$endif}
result := a + b;
// 模拟工作1秒
sleep(1000);
end;
// 返回:
// 29-06-2020 22:58:45.808 [chrono] tcalculator.sum -> sum 100 + 50 = 1,00s
计算从点到点以及退出函数的处理时间:
function tcalculator.divide(a, b: int64): double;
begin
{$ifdef debug}
var crono := tdebugger.timeit(self, 'divide', format('divide %d / %d', [a, b]));
{$endif}
result := a / b;
// 模拟工作500毫秒
sleep(500);
{$ifdef debug}
crono.breakpoint('only divide');
{$endif}
// 模拟工作1秒
sleep(1000);
{$ifdef debug}
crono.breakpoint('only sleep');
{$endif}
end;
// 返回:
// 29-06-2020 23:25:46.223 [chrono] tcalculator.divide -> first point = 500,18ms
// 29-06-2020 23:25:47.224 [chrono] tcalculator.divide -> second point = 1,00s
// 29-06-2020 23:25:47.225 [chrono] tcalculator.divide -> divide 10 / 2 = 1,50s
当进入和退出函数时获取通知,并计算时间:
function tcalculator.mult(a, b: int64): int64;
begin
{$ifdef debug}
tdebugger.enter(self, 'mult').timeit;
{$endif}
result := a * b;
// 模拟工作2秒
sleep(2000);
end;
// 返回:
// 29-06-2020 22:58:45.808 [enter] >> tcalculator.mult
// 29-06-2020 22:58:47.810 [exit] >> tcalculator.mult in 2,00s
使用命令行扩展,可以轻松处理命令行参数。
定义一个从tparameters或tserviceparameters(如果使用quickappservices)继承的类,将可能的参数作为已发布的属性:
uses
quick.parameters;
type
tcommand = (copy, move, remove);
tmymode = (mdadd, mdselect, mdremove);
[commanddescription('使用quick.parameters的简单控制台应用程序示例')]
tmyparameter = class(tparameters)
private
// ... [私有成员]
published
// ... [已发布的属性]
end;
使用参数:
params := tmyparameter.create;
当您使用--help调用exe时,将获得文档。如果需要检查开关或值,可以这样做:
if params.port = 0 then ... if params.silent then ...
quickparameters使用自定义属性来定义特殊的参数条件:
quickparameter会自动检查值类型。如果将参数值定义为integer,并传递了字母数字值,将引发异常。
帮助定制:
您可以使用colorizehelp定义自己的颜色定制。如果enabled属性为true,则使用自定义颜色,否则使用黑白颜色。
parameters.colorizehelp.enabled := true; parameters.colorizehelp.commandname := cccyan; parameters.colorizehelp.commandusage := ccblue;
当参数检测到帮助参数时,将显示帮助文档。
parameters.showhelp: 显示自动生成的帮助文档。
常用验证工具。
(此部分未给出具体示例代码)
以流畅风格进行前置条件和后置条件的验证。
condition.requires在执行某些操作之前评估变量的条件。
condition.ensures在执行某些操作之后评估变量结果的条件。
condition.requires(num, "num")
.isinrange(1, 10, 'value for num is out of range'); // 如果不在范围内,则抛出自定义错误
.isnotgreater(50); // 如果不等于50,则抛出argumentexception
condition.requires(myobj, "myobj")
.withexceptiononfailure(emyexception) // 如果失败,则抛出特定异常
.isnotnull(); // 如果为null,则抛出argumentnullexception
.evaluate(myobj.id > 10); // myobj.id必须大于10
condition.requires(text, "text")
.isnotempty(); // 如果为空,则抛出argumentnullexception
.startswith("<html>") // 如果不以<html>开头,则抛出argumentexception
.endswith("</html>") // 如果不以</html>结尾,则抛出argumentexception
.isnotlowercase; // 如果不是小写,则抛出argumentexception
.evaluate(text.contains("sometxt") or text.contains('othertxt')); // 如果不满足条件,则抛出argumentexception
你想学习delphi或提高你的技能吗?访问 learndelphi.org
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论