Reputation: 33232
I would like to do something like this but it won't compile because Pair cannot be assigned to.
var
MyDictionary: TDictionary<TGuid, TCustomRecord>;
Pair: TPair<TGuid, TCustomRecord>;
begin
// ... create and populate my dictionary ...
foreach Pair in MyDictionary do
begin
PairRef.Value.MyField := PairRef.Value.MyField + 1;
end;
end
Just to be clear, I know how to accomplish this with more code, I'm looking for something that is concise and easy to read.
Upvotes: 5
Views: 1842
Reputation: 612964
There is no iterator on TDictionary
that returns a reference to a value. All the iterators provide values and that means that what you are asking for is not possible with the current design.
In other languages, for example C++ and D that I know, references are first class citizens in the language. You can easily write iterators that enumerate references rather than values. That's what you need to solve your problem concisely. Unfortunately the language is lacking.
One obvious option would be to switch to using reference types (class) rather than value types (record). That would solve the iteration problem in a stroke because would be iterating over references. However, one usually chooses to use value types for a good reason and you may have constraints that stop you making this switch.
Another possibility would be to write a container that offered iterators that provided pointers to the values. That's as close as you can get to a reference to a record. But you would have to roll your own container.
Upvotes: 1
Reputation: 19106
Here is a simple program which shows the different handling using records and objects with a TDictionary
.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Generics.Collections;
type
TMyRecord = record
Field : Integer;
end;
TMyObject = class
Field : Integer;
end;
procedure UseObjectDict;
var
LDict : TDictionary<TGUID, TMyObject>;
LValue : TMyObject;
begin
write( 'TMyObject: ' );
LDict := TObjectDictionary<TGUID, TMyObject>.Create( [doOwnsValues] );
try
// populate
while LDict.Count < 10 do
begin
LDict.Add( TGuid.NewGuid, TMyObject.Create );
end;
// update
for LValue in LDict.Values do
begin
LValue.Field := LValue.Field + 1;
end;
// output
for LValue in LDict.Values do
begin
write( LValue.Field, ', ' );
end;
Writeln;
finally
LDict.Free;
end;
end;
procedure UseRecordDict;
var
LDict : TDictionary<TGUID, TMyRecord>;
LKey : TGUID;
LValue : TMyRecord;
begin
write( 'TMyRecord: ' );
LDict := TDictionary<TGUID, TMyRecord>.Create;
try
// populate
while LDict.Count < 10 do
begin
LValue.Field := 0;
LDict.Add( TGuid.NewGuid, LValue );
end;
// update
for LKey in LDict.Keys do
begin
LValue.Field := LDict[LKey].Field + 1;
LDict.AddOrSetValue( LKey, LValue );
end;
// output
for LValue in LDict.Values do
begin
write( LValue.Field, ', ' );
end;
Writeln;
finally
LDict.Free;
end;
end;
begin
ReportMemoryLeaksOnShutdown := True;
try
UseObjectDict;
UseRecordDict;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Upvotes: 8