Delphi Programming Guide
Delphi Programmer 

Menu  Table of contents
Bookmark and Share

Part I - Foundations
  Chapter 1 Delphi 7 and Its IDE
  Chapter 2 The Delphi Programming Language
  Chapter 3 The Run-Time Library
  Chapter 4 Core Library classes
  Chapter 5 Visual Controls
  Chapter 6 Building the User Interface
  Chapter 7 Working with Forms
Part II - Delphi Object-Oriented Architectures
  Chapter 8 The Architecture of Delphi Applications
  Chapter 9 Writing Delphi Components
  Chapter 10 Libraries and Packages
  Chapter 11 Modeling and OOP Programming (with ModelMaker)
  Chapter 12 From COM to COM+
Part III - Delphi Database-Oriented Architectures
  Chapter 13 Delphi's Database Architecture
  Chapter 14 Client/Server with dbExpress
  Chapter 15 Working with ADO
  Chapter 16 Multitier DataSnap Applications
  Chapter 17 Writing Database Components
  Chapter 18 Reporting with Rave
Part IV - Delphi, the Internet, and a .NET Preview
  Chapter 19 Internet Programming: Sockets and Indy
  Chapter 20 Web Programming with WebBroker and WebSnap
  Chapter 21 Web Programming with IntraWeb
  Chapter 22 Using XML Technologies
  Chapter 23 Web Services and SOAP
  Chapter 24 The Microsoft .NET Architecture from the Delphi Perspective
  Chapter 25 Delphi for .NET Preview: The Language and the RTL
       
  Appendix A Extra Delphi Tools by the Author
  Appendix B Extra Delphi Tools from Other Sources
  Appendix C Free Companion Books on Delphi
       
  Index    
  List of Figures    
  List of tables    
  List of Listings    
  List of Sidebars  

 
Previous Section Next Section

Type-Safe Down-Casting

The Delphi type-compatibility rule for descendant classes allows you to use a descendant class where an ancestor class is expected. As I mentioned earlier, the reverse is not possible. Now, suppose the TDog class has an Eat method, which is not present in the TAnimal class. If the variable MyAnimal refers to a dog, it should be possible to call the function. But if you try, and the variable is referring to another class, the result is an error. By making an explicit typecast, you could cause a nasty run-time error (or worse, a subtle memory overwrite problem), because the compiler cannot determine whether the type of the object is correct and the methods you are calling actually exist.

To solve the problem, you can use techniques based on run-time type information (RTTI, for short). Essentially, because each object "knows" its type and its parent class, you can ask for this information with the is operator (or in some peculiar cases using the InheritsFrom method of TObject). The parameters of the is operator are an object and a class type, and the return value is a Boolean:

if MyAnimal is TDog then ...

The is expression evaluates as True only if the MyAnimal object is currently referring to an object of class TDog or a type descendant from TDog. This means that if you test whether a TDog object is of type TAnimal, the test will succeed. In other words, this expression evaluates as True if you can safely assign the object (MyAnimal) to a variable of the data type (TDog).

Now that you know for sure that the animal is a dog, you can make a safe typecast (or type conversion). You can accomplish this direct cast by writing the following code:

var
  MyDog: TDog;
begin
  if MyAnimal is TDog then
  begin
    MyDog := TDog (MyAnimal);
    Text := MyDog.Eat;
  end;

This same operation can be accomplished directly by the second RTTI operator, as, which converts the object only if the requested class is compatible with the actual one. The parameters of the as operator are an object and a class type, and the result is an object converted to the new class type. You can write the following snippet:

MyDog := MyAnimal as TDog;
Text := MyDog.Eat;

If you only want to call the Eat function, you might also use an even shorter notation:

(MyAnimal as TDog).Eat;

The result of this expression is an object of the TDog class data type, so you can apply to it any method of that class. The difference between the traditional cast and the use of the as cast is that the second approach raises an exception if the object type is incompatible with the type you are trying to cast it to. The exception raised is EInvalidCast (exceptions are described at the end of this chapter).

To avoid this exception, use the is operator and, if it succeeds, make a plain typecast (in fact, there is no reason to use is and as in sequence, doing the type check twice):

if MyAnimal is TDog then
  TDog(MyAnimal).Eat;

Both RTTI operators are very useful in Delphi because you often want to write generic code that can be used with several components of the same type or even of different types. When a component is passed as a parameter to an event-response method, a generic data type is used (TObject); so, you often need to cast it back to the original component type:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Sender is TButton then
    ...
end;

This is a common technique in Delphi, and I'll use it in examples throughout the book. The two RTTI operators, is and as, are extremely powerful, and you might be tempted to consider them as standard programming constructs. Although they are indeed powerful, you should probably limit their use to special cases. When you need to solve a complex problem involving several classes, try using polymorphism first. Only in special cases, where polymorphism alone cannot be applied, should you try using the RTTI operators to complement it. Do not use RTTI instead of polymorphism. This is bad programming practice, and it results in slower programs. RTTI has a negative impact on performance, because it must walk the hierarchy of classes to see whether the typecast is correct. As you have seen, virtual method calls require just a memory lookup, which is much faster.

Note 

Run-time type information (RTTI) involves more than the is and as operators. You can access detailed class and type information at run time, particularly for published properties, events, and methods. You'll find more on this topic in Chapter 4.


 
Previous Section Next Section


 


 


Copyright © 2004-2017 "Delphi Sources". Delphi Programming Guide
   Facebook     Twitter