views:

1655

answers:

4

I have a procedure that expects a parameter of type TObject, something like this:

MyProcedure (const AValue : TObject);

I have an array of Variant that I'm looping through to call the procedure, something like this:

  for i:=0 to High(myArray) do
    MyProcedure (myArray[i]);

The compiler gives an error saying: "Incompatible types: TObject and Variant".

What can I do to get around this?

More information: Up until now, I have been passing simple types (strings, numbers, dates) in variant arrays (the arrays are typically a mix of different types -- I'm eventually passing them as parameters to a database stored procedure). Now I need to also (in certain cases) pass a TObject.

What is the most appropriate data type/structure to pass the values, that can hold both simple types and objects? I guess I could create my own TParam type that has a field for both, but I am not sure of the exact syntax. Anyone has an example of this?

+1  A: 

My first reaction is to ask why you are storing TObjects in a list of variants, but assuming you have a good reason!

If you managed to place your TObject instance in the Array in the first place then you probably placed the pointer to the Tobject. In that case what you need to do is typecast the Variant/Integer as a TObject eg

for i:=0 to High(myArray) do
    MyProcedure (TObject(myArray[i]));

This should work, however as with any typecasting it is up to you to ensure that myArray[i] is actually a pointer to a TObject instance or horrible things might happen.

Are you sure a TList would not do what you want. A variant is really meant to store fundamental types such as string, integer, float, boolean not Objects.

Toby Allen
+3  A: 

You can't store plain objects is a variant. But you can store interfaces.

var
  v : Variant;
  i : IInterface;

begin
  v := i; // Works perfectly;
end.

Look at the types for the variant:

varEmpty    = $0000; { vt_empty        0 }
varNull     = $0001; { vt_null         1 }
varSmallint = $0002; { vt_i2           2 }
varInteger  = $0003; { vt_i4           3 }
varSingle   = $0004; { vt_r4           4 }
varDouble   = $0005; { vt_r8           5 }
varCurrency = $0006; { vt_cy           6 }
varDate     = $0007; { vt_date         7 }
varOleStr   = $0008; { vt_bstr         8 }
varDispatch = $0009; { vt_dispatch     9 }
varError    = $000A; { vt_error       10 }
varBoolean  = $000B; { vt_bool        11 }
varVariant  = $000C; { vt_variant     12 }
varUnknown  = $000D; { vt_unknown     13 }
//varDecimal  = $000E; { vt_decimal     14 } {UNSUPPORTED as of v6.x code base}
//varUndef0F  = $000F; { undefined      15 } {UNSUPPORTED per Microsoft}
varShortInt = $0010; { vt_i1          16 }
varByte     = $0011; { vt_ui1         17 }
varWord     = $0012; { vt_ui2         18 }
varLongWord = $0013; { vt_ui4         19 }
varInt64    = $0014; { vt_i8          20 }
varUInt64   = $0015; { vt_ui8         21 }

You can if you really want, cast TObject to Pointer to Integer and store that. But i'm not sure if that's what you really want.

Gamecat
I agree with Gamecat, if you need to pass objects as variants, then I would consider using an interface.
skamradt
+7  A: 

A Variant cannot hold objects, it can only contain primitive types such as integer and string.

I would suggest changing your array to be of the type you want rather than variant. If you are not sure of the object type you want then create an array of TObject or the lowest possible base class of the objects that your array will hold.

Steve Claridge
Delphi's custom Variants (TCustomVariantType descendants) can hold objects. There are even examples of this in the VCL, see SqlTimSt.TSQLTimeStampVariantType, FMTBcd.TFMTBcdVariantType.
TOndrej
+1  A: 

I don't think it's a good idea to create a TParam with a Variant variable a TOBject variable. Something like:

Tobject = record
  prim: Variant;
  obj: TObject;
end

Would work but it seems very messy to me. Pascal is not a dyanmically typed language and I woudn't try and treat it as one. I would suggest either:

Create another function to handle Variant, so as well as having

MyProcedure (const AValue : TObject);

also have

MyProcedure (const AValue : Variant);

and handle your data seperately. Or create a record that defines your data inputs, for example instead of having TParam of variant and object have something like:

TStoredProcParm = record
  name: String;
  bought: TDateTime;
end;

and so on. Maybe you have so much different data that the variant/object route is the best but it seems like a bit of a maintenance headache - when your other code gets passed an array of Variant how do you know which variant is which and how to use each of them in your Stored proc?

Steve Claridge
I see your point about the TParam record being "messy". But I have a way of knowing when to use which, so this is probably the best solution for me on this project (the TObject value will be used very rarely).
ObiWanKenobi