views:

1453

answers:

3

Why can't Delphi variants hold objects? More importantly, what's the reason behind this limitation?

+4  A: 

This is just an opinion, from my experience with what variants can and cannot do.

If you put a COM object into it, it will be stored as an IDispatch reference, and thus any method calls or properties you access on this object will be converted into some code that looks up the internal DISPID of the method/property, an array with method arguments will be constructed, and the method will be invoked through the IDispatch interface.

In other words, IDispatch is handled for you, the way you would normally have to do it, but it's done automagically by the compiler.

However, for normal Delphi objects, things get harder. You can use RTTI to find, and call published methods and properties, but that's about it. If you have the name of a non-published, non-virtual method, Delphi can't find the right address for it on your method.

In other words, all you would be able to do would be to just hold the object, you wouldn't be able to use it. Perhaps they could add support for just freeing it, but again, that would probably be it.

I know for a fact that if you implement IDispatch correctly, you can safely store, and use the object through a variant. I have a class that can be used as the base class for Delphi objects you want to do this on. It will automatically expose published methods/properties, and you can add more if you want through some protected method calls. If there is an interest in such a class I can place it somewhere.

But again, this is through IDispatch, and it uses the published methods, the rest is manual code, so the support for variants have to be built into your objects, by you.

Which is why I think they just said: This will just generate complaints, that we can hold an object but it's just useless.

But that's just my thoughts. Perhaps someone official have a much better answer.

Lasse V. Karlsen
You're pretty much right. You should also mention the TCustomVariantType and TInvokeableVariantType classes. Those classes show all the things a class would have to do to be able to be *used* (not just stored) in a Variant, without necessarily implementing IDispatch.
Rob Kennedy
+10  A: 

You can definitely store an object inside a Variant variable - just cast it into a cardinal. An object is just a pointer, anyway.

Assert(SizeOf(pointer) = SizeOf(cardinal)); //64-bit safety check
v := cardinal(obj);
obj := TSomeObject(cardinal(v));
gabr
+3  A: 

I had used Variants to hold objects in the past using the Variant internals, the code is something like this:

var
  MyObject: TMyObject;
  Value: Variant;
begin
  MyObject:= TMyObject.Create;
  TVarData(Value).VType:= VarByRef;
  TVarData(Value).VPointer:= MyObject;
Cesar Romero
Note that you're cheating. VarByRef is a *flag* meant to modify the underlying type field. Your code says the variable is *empty*, but it's empty by reference.
Rob Kennedy
Hmmm? Rob, this mean that varIsEmpty would return True???
Fabricio Araujo
Conceptually it's OK I think; logically, an untyped reference to a location is equivalent to an untyped pointer, i.e. void* in C or Pointer in Delphi.
Barry Kelly