不知道cnblogs的机制是什么,根据后台管理的页面浏览与RSS的统计,“随笔”的阅读量要高于“文章”。不过就内容来说,“文章”的价值要远高于“随笔”的,尤其是对初学者来说。因为随笔一般只针对几个语法知识点,而“文章”的内容则要更基本一些——当然也许这样也导致“文章”会更抽象、晦涩一些,不是很好读懂。一两个语法知识点只能够帮助你更好的应用Delphi,而基础知识对于基于过程的编程语言来说,大体上是通用的——也就是说,到目前为止,“随笔”更多讲的是Delphi特性的东西,而“文章”讲共性的东西更多一些。如果你对自己的基础并不完全自信的话,希望你能读一下目前已经有的“文章”:
作为一门基于过程的语言,Delphi的内容无非包括以下几种:类型,变量,函数。熟练掌握它们,再对Delphi的类库等有足够的了解,就已经可以写出不错的程序了。如果要弄一些更接近操作系统的内容,那就要对WinAPI有一定程度的认识,熟练使用MSDN能快速找到需要的函数;喜欢GUI的话,就要了解基本的GDI对象,理解Windows的消息机制,对VCL的封装模型也要有一定概念。这个时候,就基本上可以独立解决大部分问题,算得上是半个“高手”了。不对是使用类、使用DLL,还是应用接口等等,完全还是围绕类型、变量、函数这几样东西转,只不过形式稍微复杂一些。
我把对Delphi的使用分为三个层级:初级时,对类型、变量、过程只有初步的认识,还不能熟练的匹配;中级时,则要能够有清晰的认识,能够熟练匹配,并且意识到脱离不开它们;到了高级,就可以更意识流一些,眼里看到的只有内存,类型、变量、过程都是为了方便使用才存在的,能够熟练的在看似不同的类型之间进行正确的转换。如果不太高级是什么意思的话,这里可以举个例子:某次写一个小程序,需要计算文件的MD5。开始我直接使用了CnVCL(过去叫CnPack)的CnMD5单元,在处理一个大文件的时候,发现处理速度明显要比用Total Commander的一个插件慢。于是我又对比了一下.Net的库,发现同样也比CnMD5快很多。由于.net直接使用了AdvApi32.dll的导出函数,于是我根据网上搜到的导出函数原型,写出了以下的Delphi声明:
type TAdv32MD5 = record TotalSize : Int64; buffDigest : array[0..3]of LongWord; buffInput : array[0..63]of Byte; Digest : array[0..3]of LongWord; end; const AdvApi32 = 'AdvApi32.dll'; procedure aaMD5Init(var MD5Context: TAdv32MD5); stdcall; external AdvApi32 name 'MD5Init'; procedure aaMD5Update(var MD5Context: TAdv32MD5; const Buffer; Size: LongWord); stdcall; external AdvApi32 name 'MD5Update'; procedure aaMD5Final(var MD5Context: TAdv32MD5); stdcall; external AdvApi32 name 'MD5Final';
这样当然是可以正确使用的。不过我用的是D2010,2006后版本的Delphi支持记录类型的成员函数,可以封装一下。于是我利用这个语法支持,写下了另一个版本的声明,使用起来会方便很多:
type TMD5dig = record Digest : array[0..3]of LongWord; function ToString: string; end; TAdv32MD5 = record private TotalSize : Int64; buffDigest : array[0..3]of LongWord; buffInput : array[0..63]of Byte; public digest : TMD5dig; constructor Create(const Buffer; Size: LongWord); procedure Hash(const Buffer; Size: LongWord); class function MD5(const Buffer; Size: LongWord): TMD5dig; static; procedure Initialize; stdcall; procedure Update(const Buffer; Size: LongWord); stdcall; procedure FinalDigest; stdcall; end; const AdvApi32 = 'AdvApi32.dll'; { TMD5dig } function TMD5dig.ToString: string; const HexChars : array[0..15]of Char = '0123456789ABCDEF'; var i, b : LongWord; pSrc : PAnsiChar; pDst : PChar; begin SetLength(Result, 32); pDst := Pointer(Result); pSrc := @Self; for i:=0 to 15 do begin b := Ord(pSrc[i]); pDst[i*2+0] := HexChars[b shr $4]; pDst[i*2+1] := HexChars[b and $f]; end; end; { TAdv32MD5 } constructor TAdv32MD5.Create(const Buffer; Size: LongWord); begin Initialize; Update(Buffer, Size); end; procedure TAdv32MD5.FinalDigest; external AdvApi32 name 'MD5Final'; procedure TAdv32MD5.Hash(const Buffer; Size: LongWord); begin Initialize; Update(Buffer, Size); FinalDigest; end; procedure TAdv32MD5.Initialize; external AdvApi32 name 'MD5Init'; class function TAdv32MD5.MD5(const Buffer; Size: LongWord): TMD5dig; var tmp: TAdv32MD5; begin tmp.Hash(Buffer, Size); Result := tmp.digest; end; procedure TAdv32MD5.Update(const Buffer; Size: LongWord); external AdvApi32 name 'MD5Update';
观察Initialize、Update以及FinalDigest的声明与实现部分,并没有声明一组导入函数再用成员函数去调用它们,会不会感到很意外?
此外,我还会单独讲讲字符串与编码,尤其是Unicode的问题。编码这种东西其实并不复杂,而且从Win2000开始,Windows内部就已经使用Unicode了。D2009终于原生支持Unicode版的控件了,这一天来的实在太晚了,导致大量Delphi程序员到现在还搞不清它。我始终认为,像Unicode这种东西,对拉丁或者西里尔文使用者来说意义并不大,但作为东亚国家的程序员或者码农——别怪我埋汰你,要是这么简单的东西还整不明白,也就甭笑话国足冲不出亚洲了——你跟他们在各自领域的水平半斤八两。字符串的内容稍微复杂一些,并且由于从D2009开始的变化,我也会对前后的变化进行讲解。
还要再强调一下:我主要以讲解D2010的语法为主,附带内容会提到不同版本间的语法变化(虽然我是从D4开始接触Delphi的,不过那个时候实在是个连OOP都搞不清楚的小白,所以最早提到的也只有D5到D6的变化)。千万不要觉得D2010很神秘,或者编译器本身还有很多bug,影响日常使用——我可以很明确的告诉你,目前D2010的编译器bug要么是新语法的,要么就是从老版本中带来的。当然,部分涉及网络或数据库的类库,在功能上可能会有因Unicode产生的问题,因为我几乎不写网络或数据库功能的程序(就算写大概也会选择C#),所以我无法确定D2010在这些方面到底有没有问题。由于Unicode是未来的趋势,并且据Embarcadero的路线图,未来Delphi可能会支持其它操作系统。相信我,如果连Win32平台的D2010都用不明白的话,你不可能用明白以后的新版本的。