i'm not sure if i'm getting this right, but i still remember java allowing me to cast an object to another type as long as their ancestors are the same?

I can't seem to do this in delphi, the compiler doesn't seem to understand the methods and attributes of each object even after I've casted them. What's wrong?

I'm trying to figure out how to stick two database query objects under the same name as illustrated :

qry : TObject;


if(callType = 'A') then
begin

//casting qry as a TOraQuery object :

qry := TOraQuery.Create(nil);
qry := commonObj.GetConnection('DATABASE_A');

qry.SQL.Add('DELETE FROM TEST1');
qry.ExecSQL;
qry.SQL.Clear;
end;

if(callType = 'B') then
begin

//casting qry as a TADOConnection object :

qry := TADOConnection.Create(nil);
qry := commonObj.GetConnection('DATABASE_B');

qry.SQL.Add('DELETE FROM TEST2');
qry.ExecSQL;
qry.SQL.Clear;
end;

ADrive, it may be possible that you have not fully understood just how Delphi typecasts work. First thing first - though not wrong, it does strike me as odd that you should assign the object you want to typecast as a TObject - I would have used a raw pointer. Secondly, what you have done is not really a typecast at all. I provide a few examples below which might point you in the right direction.

  • Imagine that we have a TMaskEdit component on a form
  • You want to use the OnClick event handler for this component
    []]The empty template Delphi gives you will go like this
procedure TMyForm.MaskEdit1Click(Sender:TObject)
begin
   with TMaskEdit(Sender) do
   begin
       MaxLength:=32;
       Text:='Top Secret';
   end;
end;

Here we done the typecast as TMaskEdit(Sender). The with block ensures that the properties MaxLength and Text of your TMaskEdit control can be correctly accessed.

Here is another way to do the same thing

procedure TMyForm.MaskEdit1Click(Sender:TObject)
var AEdit:TMaskEdit absolute Sender;
begin
   with AEdit do
   begin
       MaxLength:=32;
       Text:='Top Secret';
   end;
end;

Here we dispense with the explicit typecast. Instead we declare a local TMaskEdit variable which sits at the same memory location as the Sender parameter. Note that you have not created a new TMaskEdit object - you are simply referencing the Sender object in a different way.

Now look at something closer to your own example

procedure TMyForm.MyADOCode;
var qry:TObject;
//like I said, I would rather do var qry:Pointer;
begin
   qry:=TOraQuery.Create(nil);
   with TOraQuery(qry) do
   begin
       SQL.Add('Delete from Test 2');
       ExecSQL;
       Clear;
   end;
end;

I am not entirely clear just what you are trying to do but I hope this helps you make sense of Delphi typecasts.

There are a number of miscellaneous points to emphasize

  • Code such as this should normally go inside a try...finally block and you must ensure that the object you created is freed up once you are done.
  • You can reference multiple objects in a with block. For instance
procedure TMyForm.WithExample;
var AllNames:String;
      qry:TOraQuery;
      ANames:TStringList;
begin
   with qry,ANames do
   begin
       SQL.Add('Delete from Test 2');//references the SQL property of qry
       AllNames:=Text;//references the Text property of ANames  
       ExecSQL;//calls the ExecSQL method of qry
       Clear;//clears qry
   end;
end;

When you have such code Delphi scoping rules come into play. The first three lines of code raise no issues. The fourth one, Clear, is rather more complicated. Remember that both the TOraQuery and the TStringList classes have a Clear method. So which of the two objects will get cleared? Given the order in which we have specified them in the with block it will be qry. This can lead to very hard to trace bugs so be careful when writing such statements!

thanks explainthat, its really very informative.

but what i really wanted to do was to be able to use one variable to represent different types of database connection, one was TADOConnection, another TOraQuery, both sharing same qry variable.

this is because they both have some similiar method names and thus i could probably reduce my code lines by eliminating the need to do alot of conditional statements and switch between different variable types.

thanks explainthat, its really very informative.

but what i really wanted to do was to be able to use one variable to represent different types of database connection, one was TADOConnection, another TOraQuery, both sharing same qry variable.

this is because they both have some similiar method names and thus i could probably reduce my code lines by eliminating the need to do alot of conditional statements and switch between different variable types.

Like I explained, you should be able to do that with a combination of a typecast and a with statement as I have done.

var qry:Pointer;//or TObject if you want it that way
qry:=TADOConnection.Create(nil);
with TADOConnection(qry) do
begin
   //full access to TADOConnection methods and properties here
end;

with TOraQuery(qry) do
begin
   //ditto
end;

Post your actual code - not working if that happens to be the case - if this does not make complete sense and I'll correct it for you.

i'm not sure if i'm getting this right, but i still remember java allowing me to cast an object to another type as long as their ancestors are the same?

Actually you can _cast_ any object to any class, but you cannot _assign_ an ancestor-object to a variable of its descendant class.

Therefore you cannot assign TStrings to the TStringList, only vice-versa.

I can't seem to do this in delphi, the compiler doesn't seem to understand the methods and attributes of each object even after I've casted them. What's wrong?

I cannot see a single casting in your code. Casting looks like something this:

myQuery := TQuery( myOraQuery );

I'm trying to figure out how to stick two database query objects under the same name as illustrated :

I think TOraQuery and TADOConnection don't have the same ancestor that would have properties like ExecSQL and SQL. So you must use the approach suggested by ExplainThat.

//casting qry as a TOraQuery object :
qry := TOraQuery.Create(nil);
qry := commonObj.GetConnection('DATABASE_A');

This is not casting. This is merely creating an instance of the TOraQuery and than losing that reference immedietely afterwards (which is a memory leak). :(

but what i really wanted to do was to be able to use one variable to represent different types of database connection, one was TADOConnection, another TOraQuery, both sharing same qry variable.

this is because they both have some similiar method names and thus i could probably reduce my code lines by eliminating the need to do alot of conditional statements and switch between different variable types.

Unfortunately, those similar method names cannot be exploited, you'll have to duplicate your code, or use an alternative connection component.

Hope I've shed some light. :)

This article has been dead for over six months. Start a new discussion instead.