166人参与 • 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 举报,一经查实将立刻删除。
发表评论