请问什么时候使用as关键字来进行强制类型转换?
我在书上看到下面这个例题:
procedure Foo(Anobject:TObject);1、(Foo as TEdit).Text:='Hello World';
这里是否可以用TEdit(Foo).Text:='Hello World';?
2、if (Foo is TEdit) then
TEdit(Foo).Text:='Hello World';
[在书上讲到:这个例子中不能用as进行强制类型转换。]
但这是为什么呢?为什么不可以用(Foo as TEdit).Text:='Hello World'; ?
我在书上看到下面这个例题:
procedure Foo(Anobject:TObject);1、(Foo as TEdit).Text:='Hello World';
这里是否可以用TEdit(Foo).Text:='Hello World';?
2、if (Foo is TEdit) then
TEdit(Foo).Text:='Hello World';
[在书上讲到:这个例子中不能用as进行强制类型转换。]
但这是为什么呢?为什么不可以用(Foo as TEdit).Text:='Hello World'; ?
var
ch: Char;TEdit(ch).Caption = '';这样的代码是可以通过编译的但是跑起来就要出内存引用错误。而as 则是动态的类型转换,主要用于接口的转换,把一个接口转换为另外一个接口。假如转换失败,会抛出异常。另外,像ch as TButton 这样的转换是不会被编译器通过的,因为它认为这样的转换类型不符。
我在书上看到下面这个例题:
procedure Foo(Anobject:TObject);1、(Anobject as TEdit).Text:='Hello World';
这里是否可以用TEdit(Anobject).Text:='Hello World';?2、if (Anobject is TEdit) then
TEdit(Anobject).Text:='Hello World';
[在书上讲到:这个例子中不能用as进行强制类型转换。]
但这是为什么呢?
为什么不可以用(Anobject as TEdit).Text:='Hello World'; ?楼上Spacesoft(暗夜狂沙),我看了您的回答还是有点糊涂,难道(Anobject as TEdit)与TEdit(Anobject)区别单单就是是否在编译时会抛出异常吗?还请指点,谢谢!
不光是抛出异常的问题,如果as转换的不合理,根本就编译不过去。如
var frm:TForm;如果用 (frm As Tbutton).catipn 则编译时出错。
而TButton(frm)是强类型转化。在运行时可能产生想不到的错误。
看下一个例子,可以成功运行
var i:longint;btn:TButton;
begin
btn:=Tbutton.create(self);
i:=Longint(btn);
TButton(i).Parent:=self;
TButton(i).Caption:='强类型转换';
TBUtton(i).Visible:=true;
end;
TButton(i).parent:=self;
改成 (i as TButton).parent:=self;
在编译时,提示错误:
Operator not applicable to this operand type.但后来我用as另写了一段代码:
var
i:TObject;
begin
i:=TObject.Create;
(i as TButton).Parent:=self;
(i as TButton).Caption:='强类型转换2';
(i as TButton).Visible:=true;
end;
如果i是等于TObject.create,程序编译可以通过,但运行时会报错:
Invalid class typecast如果i改写成i:=TButton.create(self),编译和运行都可以通过,程序不会报错。另外如果在上面的基础上将(i as TButton).Parent:=self;改成TButton(i).parent:=self;也可以通过呀。对这两者的区别有点糊涂了,是不是as只是用在类方面的转换?只是将一个类的对象转换成它的派生类型,并且只能转换成它的派生类,不能像TButton(i)这种方式可以将变量强制转换言之成任何类型。还请各位高手指点!谢谢!
(i as TButton).Parent:=self;
(i as TButton).Caption:='强类型转换2';
(i as TButton).Visible:=true;这样子的转换语法上是正确的,因为TButton 确实是一个TObject,打个比方:人也是一种动物,所以你把一种动物当成人看,语法上是不错的。因为(TAnimal as THuman),这个Animal可能可以跟人一样Eat(),也可能跟人一样Run(),这些都是动物可以作的,所以假如禁止这样的转换明显是不合理的。但是,假如你调用的是THuman 特有的方法呢,比如Talk(),那么就不一定可以了。这样编译期看来“不一定”的东西倒不如将他放倒运行期,跑不了就报错,这样不是更好吗?因为理智的程序员肯定不会犯((TDog as TAnimal) as THuman).Talk(); 这样的错误。
一般来说,从子类向父类转换时不会出什么问题,父类向子类转换的时候就要小心。 关于接口的问题,我一般理解接口为Delphi 的interface ,但是在这个问题里面就把我说的接口当成interface 也许不是很确切。
其中提到的
if (Anobject is TEdit) then
TEdit(Anobject).Text:='Hello World';
[在书上讲到:这个例子中不能用as进行强制类型转换。]
这个说法是错误的,可能书里的意思是在这里不用as转换也是安全的,因为前面已经用is判断过一道。正确的做法是,无用何时在进行向下转换是都应该采用as 而不是强制类型转换。向下转换是指把一个对象从父类型向子类型转换,as操作会判断这个对象是不是具体的子类型(或子类型的子类)然后转换,如果不行则会抛出异常。比如:
var
aEdit: TObject;
aBtn: TObject;
i: Integer;
begin
aEdit := TEdit.Create(nil); //因为TEdit是一个TObject的子类,所以可以直接赋值给一个TObject对象
aBtn := TButton.Create(nil);
(aEdit as TEdit).Text := 'OK'; //正常运行,因为aEdit是一个TEdit的对象
(aBtn as TEdit).Text := 'error'; //抛出异常,因为aBtn不是TEdit对象
(i as TEdit).Text := 'error'; //无法编译通过,不合法的转换
end;而强制转换一般是用来对基本类型进行转换的,比如:
var
s: string;
begin
s := 'ok'
MessageBox(0, nil, PChar(s), nil, 0);
end;
编译器会根据转换的类型的大小和具体情况决定是否可以转换,转换的规则等。而所有的对象对于编译器而言都是一个对象引用(可以近似的认为是一个指针),在两个对象引用之间进行强制转换,就是把一个地址的内容认为是转换到的类型的布局,这个转换是很不安全的,可能引发不能预测的问题。
比如,TEdit在内存中可能以这样的一张表保存(只是示例,具体的对象在内存的保存方式请查相关资料)
起始地址: Name: XXXXXXXX
偏移10字节:Text:XXXXXXXX
……………………
而TBtn可能是这样:
起始地址: Name: XXXXXXXX
偏移5字节: Caption:XXXXXX
……………………
TEdit(aBtn).Text := 'OK';执行的结果就是,在aBtn的偏移10字节处写下了'OK'的内容,然而这个操作的后果却无法预知。可能会内存越界,也可能改写了根本无意改动的数据。因此,对于对象而言,永远不要用强制转换。