Reputation: 947
I have some data types:
type
TICD10CodeMap = TObjectDictionary<string, TICD10LookupResult>;
TStringMap = TDictionary<string, string>;
TFMRecMap = TDictionary<string, TFilemanRecord>;
And some instances of them:
var
FICD10Codes: TICD10CodeMap;
FPatientTypes: TStringMap;
FPOVs: TFMRecMap;
FTreatmentTypes: TStringMap;
FTypesOfCare: TStringMap;
And I had a method that was happily populating them, using their Add methods, until I discovered that my data source could have duplicate keys in it.
Now I could just write code with ContainsKey before each and every Add() and do something, but I thought I would be clever:
procedure AddPair<ValType, DictType: TDictionary<string, ValType>>
(Key: string; Val: ValType;
Dict: DictType);
begin
if (Dict as TDictionary<string, ValType>).ContainsKey(Key) then
AddPair('Copy of ' + Key, Val, Dict)
else
Dict.Add(Key, Val);
end;
But it seems I am too clever for Delphi. First off, there's that cast in the body of the function definition, which seems like it ought to be unnecessary, then there's the fact that when I try to call AddPair
, I get compiler errors. The naive AddPair(s3, s2, FPatientTypes)
gets me both
[dcc32 Error] uReverseVistaLookups.pas(116): E2010 Incompatible types: 'ValType' and 'string'
[dcc32 Error] uReverseVistaLookups.pas(116): E2010 Incompatible types: 'DictType' and 'System.Generics.Collections.TDictionary<System.string,System.string>'
while the would-be-more-sophisticated AddPair<string, TStringMap>(s3, s2, FPatientTypes)
complains about
[dcc32 Error] uReverseVistaLookups.pas(127): E2515 Type parameter 'ValType' is not compatible with type 'System.Generics.Collections.TDictionary<System.string,System.string>'
Is there some incantation that I'm missing, which would make Delphi out of what I'm trying to do here?
Upvotes: 0
Views: 1075
Reputation: 31403
While this seems like an odd way to use a TDictionary
, an easy way to get what you want is simply to subclass.
program Project1;
{$APPTYPE CONSOLE}
uses
Generics.Collections, SysUtils;
type
TCopyKeyMap<TValue> = class(TDictionary<string, TValue>)
public
procedure AddWithCopy(const Key: string; const Value: TValue);
end;
TStringMap = TCopyKeyMap<string>;
procedure TCopyKeyMap<TValue>.AddWithCopy(const Key: string; const Value: TValue);
begin
if ContainsKey(Key) then
AddWithCopy('Copy of ' + Key, Value)
else
Add(Key, Value);
end;
var
sm : TStringMap;
sp : TPair<string, string>;
begin
sm := TStringMap.Create;
try
sm.AddWithCopy('foo', 'bar');
sm.AddWithCopy('foo', 'bat');
sm.AddWithCopy('foo', 'bam');
for sp in sm do WriteLn(Format('%s : %s', [sp.Key,sp.Value]));
finally
sm.Free;
end;
ReadLn;
end.
Upvotes: 1
Reputation: 947
D'oh.
There is no need for two type parameters in the generic:
procedure AddPair<ValType>(Key: string; Val: ValType;
Dict: TDictionary<string, ValType>);
is easy to write (without the troublesome cast!) and does what it should.
Upvotes: 3