santiagoIT
santiagoIT

Reputation: 9431

TDictionary cast fails in referenced package

A TDictionary is assigned to an TObject variable. I can cast the TOBject back to a TDictionary in the same project. However if the cast is done in a referenced bpl project the cast fails depending on how the project is referenced.

The test code:

procedure TestTDefault.Test1;
var
  Obj: TObject;
begin
  Obj := TDictionary<string, Integer>.Create;
  Check(Obj is TDictionary<string, Integer>);
  CheckNotNull(Obj as TDictionary<string, Integer>);

  // this calls the function in the referenced bpl package.
  // Returns True if Obj can be cast back to a TDictionary.
  CheckTrue(TestIfDictionary(Obj)); // outcome depends if referenced packge is included in <DCC_UsePackage>
end;

The function in the dependent package:

function TestIfDictionary(aObject: TObject): Boolean;
begin
  Result := aObject is TDictionary<string,Integer>;
end;

I have a simple project group with only two packages:

  1. The DUnit Test Runner (Package1Tests.exe)
  2. Package1.bpl

Project Group

Both packages have the same compiler/linker options set.

What is very odd is that the test works only if Package1 is NOT listed as a runtime package: Correct

However test fails if Package1 is listed a runtime package!! enter image description here

I am using XE2. As to the purpose, this issue surfaced when dealing with RTTI. I was able to isolate the problem in this simple Test project. Problem has nothing to do with RTTI. Just as a note, if instead of using a TDictionary I use a TStringList, then test always works. So problem seems to be somehow related to generics. If needed I can make the simple test project available.

I have spent quite some time tracking down this problem. I am happy that I got to discover what triggers the problem. But unfortunately I can just not understand why this happens.

Upvotes: 3

Views: 177

Answers (2)

David Heffernan
David Heffernan

Reputation: 613272

The problem you have here is related to generic instantiation. Each module is separately instantiating the generic type, and so they have different type identity. This is a basic limitation of the design of generics, and its interaction with runtime packages.

You could instantiate the generic type in a unit in the package, for instance like this:

type
  TDictionaryStringInteger = class(TDictionary<string, Integer>);

And then use TDictionaryStringInteger in place of TDictionary<string, Integer>. However, this pretty much cripples your code as it stops you writing generic methods that use generic types.

The other way out of your bind is to stop using runtime packages. Frankly, that seems rather more attractive to me.

Upvotes: 1

Zam
Zam

Reputation: 2940

You should use type declaration for this generic class.

type
 Tx = TDictionary<string, Integer>;

....

Obj := Tx.Create;
Check(Obj is Tx);

RTTI doesn't matter in your case.

Upvotes: 0

Related Questions