[转]delphi 变参函数:array of const

array of const

Delphi的Format函数大家都用得很多,第二个参数用着确实很方便。最近在数据库开发应用中需要自己创建一个带array of const参数的函数,对于常用的类型String,Integer,Pointer处理都没什么问题,但当用到Widestring类型时却出错,摸索了一上午,感觉获益良多。现在将问题、解决问题的思路、分析方法等一一道来,希望对诸君有所启发就达到了我写这篇文章的目的了!

环境:Winxp + D7

进入D7,在默认的新建工程中增加一过程Test(m: Array of const);

procedure TForm1.test(m: array of const);

var

i, zero: Integer;

s, t: String;

c: Char;

const

sBoolean: Array [Boolean] of string = ('False', 'True');

begin

s := '';

for i := 0 to High(m) do with m[i] do

case VType of //写到这,按住Ctrl点击VType,打开System单元,将VType的枚举值贴到Case语句

vtInteger: (VInteger: Integer; VType: Byte);

vtBoolean: (VBoolean: Boolean);

vtChar: (VChar: Char);

vtExtended: (VExtended: PExtended);

vtString: (VString: PShortString);

vtPointer: (VPointer: Pointer);

vtPChar: (VPChar: PChar);

vtObject: (VObject: TObject);

vtClass: (VClass: TClass);

vtWideChar: (VWideChar: WideChar);

vtPWideChar: (VPWideChar: PWideChar);

vtAnsiString: (VAnsiString: Pointer);

vtCurrency: (VCurrency: PCurrency);

vtVariant: (VVariant: PVariant);

vtInterface: (VInterface: Pointer);

vtWideString: (VWideString: Pointer);

vtInt64: (VInt64: PInt64);

end;

Delete(s, 1, 1);

Self.Caption := s;

end;

继续写,对各枚举值进行处理!这里作一下解释,Array of const正是由TVarRec类型组成的!

请看Case of语句中的代码:

vtInteger: s := s + ';' + IntToStr(VInteger);

vtBoolean: s := s + ';' + sBoolean[VBoolean];

vtChar: s := s + ';' + VChar;

vtExtended: s := s + ';' + FloatToStr(VExtended^);

vtString:

if Assigned(VString) then begin

t := VString^;

s := s + ';' + t;

end;

vtPointer:

if Assigned(VPointer) then

s := Format('%S; Pointer: $%X ',[s, Integer(VPointer)]);

vtPChar:

if Assigned(VPChar) then begin

t := VPChar^;

s := s + ';' + t;

end;

vtObject:

if Assigned(VObject) then

s := Format('%S; $%X ClassName: %S ',[s, Integer(@VObject), VObject.ClassName]);

vtClass:

if Assigned(VClass) then

s := Format('%S; Class Reference $%X - ClassName: %S ',[s, Integer(VClass), VClass.ClassName]);

vtWideChar:

begin

t := VWideChar;

s := s + ';' + t;

end;

vtPWideChar:

if Assigned(VPWideChar) then begin

t := VPWideChar^;

s := s + ';' + t;

end;

vtAnsiString:

if Assigned(VAnsiString) then begin

t := PChar(VAnsiString);

s := s + ';' + t;

end;

vtCurrency:

if Assigned(VCurrency) then

s := s + ';' + FloatToStr(VCurrency^);

vtVariant:

if Assigned(VVariant) then

s := s + '; This is variant ';

vtInterface:

if Assigned(VInterface) then

s := Format('%S; Interface: $%X',[s, Integer(VInterface)]);

vtWideString:

if Assigned(VWideString) then begin

t := PWideString(VWideString)^;

s := s + ';' + t;

end;

vtInt64:

if Assigned(VInt64) then

s := s + ';' + IntToStr(VInt64^);

加上一按钮测试该函数

procedure TForm1.Button1Click(Sender: TObject);

var

ws: WideString;

begin

ws := 'dda这是一个测试dfa';

test([self, 'sdf', 2.3324, ws, TForm]);

end;

可以看到测试结果,变量ws的值没有显示出来,怎么办呢?

我们可以看到WideString类型的值是指针,我们就从这里着手,在事件中添加一句:

Button1.Caption := Format('$%X',[Integer(@ws)]);

此句的作用是显示出ws的地址

再在Test函数中也加上类似的语句,并注释掉无用的语句:

//t := PWideString(VWideString)^;

//s := s + ';' + t;

s := s + ';' + Format('$%X',[Integer(VWideString)]);

运行可看到二个地址不一样,说明Delphi对传入的参数数据作了复制

因此将其强制转换成PWidechar应该可以,增加一变量声明

w: WideString;

w := PWideString(VWideString)^;

s := s + ';' + w;

但运行结果却只显示一个字符,别沮丧,已经摸到门道了!

我们知道Format可以处理Widestring类型,这里只得到一个字符,说明字符被截断了。Delphi中的字符串是以#0结束,Widestring以二个#0结束,可以肯定w := PWideString(VWideString)^这句Delphi作转换时肯定将其默认作为AnsiString处理了。分析到这里已经可动手写下去了.....

p: PByte;

if Assigned(VWideString) then begin

t := '';

zero := 0;

p := VWideString;

repeat

c := char(p^);

inc(p);

if c = #0 then

inc(zero) else

begin

zero := 0;

t := t + c;

end;

until zero = 2;

s := s + ';' + t;

end;

但是显示汉字却变成乱码了,而且处理也显得臃肿。到这里我们已经明白了,VWideString所指示的字符串是二字节宽字符串,而且Intel的字节顺序也是低位在前,高位在后。因此可用PWord进行处理!

删除c,zero,w变量,p改成:

p: PWord;

if Assigned(VWideString) then begin

t := '';

p := VWideString;

repeat

t := t + widechar(p^);

inc(p);

until p^ = 0;

s := s + ';' + t;

end;

可以看到核心代码已经很精练了,运行已经显示正常,汉字也无乱码了!至此我们似乎是大功告成了,但静下来想想,Delphi支持WideString到String的转换,它也应该有这样的处理代码。

而且在循环中t := t + widechar(p^);语句处下一断点,运行到断点处,再打开CPU窗口,看到看似简洁的代码,单此一句,编译器都要给它加上一大堆处理代码。找到系统的字符串处理函数很有必要,经过在System.pas单元中搜索WideString,找到函数:procedure WideCharToStrVar(Source: PWideChar; var Dest: string);

呵呵,这正是我们要的!!!

现在循环语句及P变量都可删除了,代码我就省略了。

function MakeStr(const Args: array of const): string;

const

BoolChars: array[Boolean] of Char = ('F', 'T');

var

I: Integer;

begin

Result := '';

for I := 0 to High(Args) do

with Args[I] do

case VType of

vtInteger: Result := Result + IntToStr(VInteger);

vtBoolean: Result := Result + BoolChars[VBoolean];

vtChar: Result := Result + VChar;

vtExtended: Result := Result + FloatToStr(VExtended^);

vtString: Result := Result + VString^;

vtPChar: Result := Result + VPChar;

vtObject: Result := Result + VObject.ClassName;

vtClass: Result := Result + VClass.ClassName;

vtAnsiString: Result := Result + string(VAnsiString);

vtCurrency: Result := Result + CurrToStr(VCurrency^);

vtVariant: Result := Result + string(VVariant^);

vtInt64: Result := Result + IntToStr(VInt64^);

end;

end;

We can call this function using an open array constructor (see Open array constructors).

For example,

MakeStr(['test', 100, ' ', True, 3.14159, TForm])

returns the string "test100 T3.14159TForm".