您的位置:首页 > 编程语言 > Delphi

Delphi中避免使用ClassName判断对象的类型

2008-03-13 20:22 246 查看
首先,我们知道多态是面向对象的三大特性之一。所谓多态,其思想就是,对于不同的具体类型,我们可以通过相同的抽象接口进行访问,而不必关系具体类型的实现细节。就像下达通知:所有员工明天9点在人民广场集合。并不需要具体通知每个住在不同位置的人应该几点出发,走什么路线,因为这是具体的人的责任,而非通知下达者的责任。所以,在写到需要判断ClassName进行分别处理的时候,首先应该想到的处理方式是在父类中增加接口,通过子类override完成。如下面改变把图形大小的代码:

2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694

    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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: