有一种简洁的方法来更新所有记录类型TDictionary中的值吗?

4
我想做类似的事情,但由于无法分配给“Pair”,所以它无法编译。
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

明确一点,我知道如何用更多的代码完成这项任务,我正在寻找一些简洁易读的方法。


你可以使用 TDictionary<T>.Values。 - Sir Rufo
你想在for循环中修改键或值吗?如果你只想操作值,可以使用按项迭代器for CustomRecord in MyDictionary(其中CustomRecord:TCustomRecord)。 - Anya Shenanigans
@SirRufo,Values是一个只读枚举器。 - Lawrence Barsanti
@SirRufo,你说得对。但我不想将它更改为对象,因为那样我就必须开始担心内存泄漏的问题。该记录并不总是在字典中使用,并且它经常被传递(通常使用const)。 - Lawrence Barsanti
5
使用TObjectDictionary代替,并在构造函数中传递[doOwnsValues]参数;这将使得TObjectDictionary在提取、删除或销毁字典时自动释放Values(类似于TObjectList的行为)。然后,您可以使用一个类而不是一个记录,就不会有这个问题了。 - Ken White
显示剩余3条评论
2个回答

8
这里有一个简单的程序,展示了使用记录和对象来处理 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.

1

TDictionary没有返回值的迭代器。所有的迭代器都提供值,这意味着你所要求的在当前设计下是不可能实现的。

在其他编程语言中,比如我知道的C++和D,引用是语言中的一等公民。你可以很容易地编写枚举引用而不是值的迭代器来解决你的问题。不幸的是,这种语言缺乏。

另一个明显的选择是切换到使用引用类型(类)而不是值类型(记录)。这将在一次转换中解决迭代问题,因为你将会枚举引用。然而,通常选择使用值类型是有充分理由的,你可能有阻止你进行此转换的限制。

另一种可能性是编写一个容器,提供指向值的指针的迭代器。这是最接近记录引用的方式。但你必须自己编写容器。


糟糕,这不是我所期望的答案。 - Lawrence Barsanti
这可能看起来很丑,但我想知道是否可以通过创建指向记录的指针的TDictionary来解决这个问题。(很恶心,我知道) - Warren P
@Warren 但是谁会拥有这些记录?你需要为它们准备一个单独的容器。 - David Heffernan
1
是的,我想这就是一路糟糕。 - Warren P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接