Delphi中避免使用ClassName判断对象的类型
2008-04-30 10:19
253 查看
在公司原有系统的代码中,我看到了很多判别对象的ClassName属性进行分别处理的代码。而且似乎已经是处理类似问题的标准方法。但是其中可能会隐含一些问题。
首先,我们知道多态是面向对象的三大特性之一。所谓多态,其思想就是,对于不同的具体类型,我们可以通过相同的抽象接口进行访问,而不必关系具体类型的实现细节。就像下达通知:所有员工明天9点在人民广场集合。并不需要具体通知每个住在不同位置的人应该几点出发,走什么路线,因为这是具体的人的责任,而非通知下达者的责任。所以,在写到需要判断ClassName进行分别处理的时候,首先应该想到的处理方式是在父类中增加接口,通过子类override完成。如下面改变把图形大小的代码: for i := 0 to 图形列表.Count - 1 do begin 图形 := 图形列表[i]; if 图形.ClassName = '长方形' then begin 长方形(图形).长 := 长方形(图形).长 * 2; 长方形(图形).宽 := 长方形(图形).宽 * 2; end else if 图形.ClassName = '圆形' then begin 圆形(图形).半径 := 长方形(图形).半径 * 2; end end; 就可以在父类“图形”中增加“ChangeSize”方法,代码如下 图形 = class …… procedure ChangeSize(rate: Integer); virtual; end; 长方形 = class …… procedure ChangeSize(rate: Integer); override; end; 圆形 = class …… procedure ChangeSize(rate: Integer); override; end;在具体的图形类中实现大小改变的代码: procedure 长方形.ChangeSize(rate: Integer); begin 长 := 长 * rate; 宽 := 宽 * rate; end; procedure 圆形.ChangeSize(rate: Integer); begin 半径 := 半径 * rate; end;这样修改后,上面的代码就可以这样调用了: for i := 0 to 图形列表.Count - 1 do begin 图形 := 图形列表[i]; 图形.ChangeSize(2); end;这样代码的意图清晰了很多。
当然,在很多时候,出现判断ClassName的情况下并不能采用上边的解决方法。比如遍历Form的Cotrols并对不同的控件进行分别初试化。我们不可能去TControl中增加初始化方法,只有采用判别具体子类类型。那么这时我推荐采用is运算符而非直接比较ClassName。is的用法,语句 aObject is TForm 在不同的aObject的类型情况下结果如下: aObject是TObject,结果为假; aObject是TForm,结果为真; aObject是TForm1,结果为真; aObject是TEdit,结果为假; aObject是nil,结果为假;从上面示例可以看到采用is的一个优点,is可以判断是否子类的情况,比如我们在初始化控件的时候根据是TImage还是TEdit作不同的初始化,通过is判断处理。将来也许会采用TCoolEdit来美化界面,那么这段代码不需要更改,因为一个TCoolEdit是一个TEdit;而如果采用ClassName那么必须更改为子类的名字才行。
其次如果被判断的对象有可能为空,使用ClassName判断必须先判断对象是否赋值,否则就会出现内存访问错误。判断代表必须写为:if Assigned(aObject) and aObject.ClassName = 'TClass1';而采用is只需要写为if aObject is TClass1。最后一个不采用ClassName作为判定的原因是,ClassName只是用来描述一个类的属性,字符串比较不能在编译期获得检查,如果存在拼写错误,或是大小写问题代码都会出现逻辑错误,而这种错误只有在运行期运行到这一语句的时候才会被发现。 if aControl.ClassName = 'TEidt' then //只有在你注意到Edit没有初试化时才会来检查这段代码; if aControl is TEidt then //无法编译通过;
综合上面所述,在需要判定一个对象的具体类型时,首先应该考虑通过多态处理避免这种分别特殊处理的语句,实在不能避免的情况下应该采用is运算符判断,而非ClassName。在一种很特殊的情况下,is可能不能得到想要的结果,比如需要分别处理TEdit和TCoolEdit的情况,用is的话CoolEdit也会判断为TEdit,这时可以采用ClassType方法,也要胜过没有类型检测的字符串比较: aCoolEdit is TEdit //True; aCoolEdit.ClassType = TEdit //False; aCoolEdit.ClassType = TCoolEdit //True;
首先,我们知道多态是面向对象的三大特性之一。所谓多态,其思想就是,对于不同的具体类型,我们可以通过相同的抽象接口进行访问,而不必关系具体类型的实现细节。就像下达通知:所有员工明天9点在人民广场集合。并不需要具体通知每个住在不同位置的人应该几点出发,走什么路线,因为这是具体的人的责任,而非通知下达者的责任。所以,在写到需要判断ClassName进行分别处理的时候,首先应该想到的处理方式是在父类中增加接口,通过子类override完成。如下面改变把图形大小的代码: for i := 0 to 图形列表.Count - 1 do begin 图形 := 图形列表[i]; if 图形.ClassName = '长方形' then begin 长方形(图形).长 := 长方形(图形).长 * 2; 长方形(图形).宽 := 长方形(图形).宽 * 2; end else if 图形.ClassName = '圆形' then begin 圆形(图形).半径 := 长方形(图形).半径 * 2; end end; 就可以在父类“图形”中增加“ChangeSize”方法,代码如下 图形 = class …… procedure ChangeSize(rate: Integer); virtual; end; 长方形 = class …… procedure ChangeSize(rate: Integer); override; end; 圆形 = class …… procedure ChangeSize(rate: Integer); override; end;在具体的图形类中实现大小改变的代码: procedure 长方形.ChangeSize(rate: Integer); begin 长 := 长 * rate; 宽 := 宽 * rate; end; procedure 圆形.ChangeSize(rate: Integer); begin 半径 := 半径 * rate; end;这样修改后,上面的代码就可以这样调用了: for i := 0 to 图形列表.Count - 1 do begin 图形 := 图形列表[i]; 图形.ChangeSize(2); end;这样代码的意图清晰了很多。
当然,在很多时候,出现判断ClassName的情况下并不能采用上边的解决方法。比如遍历Form的Cotrols并对不同的控件进行分别初试化。我们不可能去TControl中增加初始化方法,只有采用判别具体子类类型。那么这时我推荐采用is运算符而非直接比较ClassName。is的用法,语句 aObject is TForm 在不同的aObject的类型情况下结果如下: aObject是TObject,结果为假; aObject是TForm,结果为真; aObject是TForm1,结果为真; aObject是TEdit,结果为假; aObject是nil,结果为假;从上面示例可以看到采用is的一个优点,is可以判断是否子类的情况,比如我们在初始化控件的时候根据是TImage还是TEdit作不同的初始化,通过is判断处理。将来也许会采用TCoolEdit来美化界面,那么这段代码不需要更改,因为一个TCoolEdit是一个TEdit;而如果采用ClassName那么必须更改为子类的名字才行。
其次如果被判断的对象有可能为空,使用ClassName判断必须先判断对象是否赋值,否则就会出现内存访问错误。判断代表必须写为:if Assigned(aObject) and aObject.ClassName = 'TClass1';而采用is只需要写为if aObject is TClass1。最后一个不采用ClassName作为判定的原因是,ClassName只是用来描述一个类的属性,字符串比较不能在编译期获得检查,如果存在拼写错误,或是大小写问题代码都会出现逻辑错误,而这种错误只有在运行期运行到这一语句的时候才会被发现。 if aControl.ClassName = 'TEidt' then //只有在你注意到Edit没有初试化时才会来检查这段代码; if aControl is TEidt then //无法编译通过;
综合上面所述,在需要判定一个对象的具体类型时,首先应该考虑通过多态处理避免这种分别特殊处理的语句,实在不能避免的情况下应该采用is运算符判断,而非ClassName。在一种很特殊的情况下,is可能不能得到想要的结果,比如需要分别处理TEdit和TCoolEdit的情况,用is的话CoolEdit也会判断为TEdit,这时可以采用ClassType方法,也要胜过没有类型检测的字符串比较: aCoolEdit is TEdit //True; aCoolEdit.ClassType = TEdit //False; aCoolEdit.ClassType = TCoolEdit //True;
相关文章推荐
- Delphi中避免使用ClassName判断对象的类型
- Delphi中避免使用ClassName判断对象的类型
- Delphi中避免使用ClassName判断对象的类型
- 如何判断对象类型/对typeof方法使用认识
- cocos2d JS 使用代码判断对象类型
- JAVASCRIPT随记-使用偏函数判断对象类型
- 使用instanceof操作符判断对象类型
- 如何使用instanceof操作符判断对象类型
- 使用Object对象的toString()方法自定义判断数据类型方法
- 丶使用is关键字判断对象是否与指定类型兼容
- 使用isinstance()来判断一个对象的类型
- (php的弱类型导致的小问题)某变量可能是数字或非数字(对象、字符串、json等)时,应该如何去判断
- QPointer,QSharedPointer,QWeakPointer的区别与使用例子(QSharedPointer类似Delphi里的引用计数,是强引用,而QWeakPointer是弱引用,不影响原始对象的引用计数,相当于是在暗中观察对象,但保持联系,需要的时候就会出现)
- 面向对象--利用toString做类型判断
- 《GOF设计模式》—适配器(ADAPTER)—Delphi源码示例:可插入的Adapter(使用代理对象)
- java中判断Object对象类型
- Python 代码优化基础——判断对象类型
- 使用stringstream对象简化类型转换
- 使用stringstream对象简化类型转换
- js判断对象的类型的四种方式