当值为空时,我能否防止TStringList删除键值对?

7

我能否防止TStringList在值为空字符串时删除键值对?我使用Delphi XE8和Lazarus,它们的工作方式不同。我希望即使值设置为空字符串,键值对也能留在TStringList对象中。例如:

procedure TMyClass.Set(const Key, Value: String);
begin
  // FData is a TStringList object
  FData.Values[Key] := Value; // Removes pair when value is empty. Count decreases and Key is lost.
end;

我遇到的问题是,当我使用Delphi编译时,空值对被删除了,我不知道后来是否有一个键的值未设置或者它被明确地设置为空字符串。此外,我无法获取所有已使用的键。现在我需要持有另一个键的集合,其中包含有关空键的信息。

MyKeyValues.Set('foo', 'bar'); // Delphi FData.Count = 1; Lazarus FData.Count = 1
MyKeyValues.Set('foo', '');    // Delphi FData.Count = 0; Lazarus FData.Count = 1

再次遇到 Embarcadero 問題... 奇怪、沒有文件記載(至少在德文文檔中)的行為。我浪費了一個小時在一個大項目中尋找這個“錯誤”。而他們甚至沒有實現切換以更改這種行為... - StanE
3个回答

4
你可以编写一个class helper来实现TStrings类的SetValue方法的新行为。
如果您不喜欢基于类助手的解决方案,可以使用继承自TStringList的自定义类,并再次覆盖其Values属性的行为 - 代码与此基于助手的实现非常相似。
我更喜欢使用第二种选择,因为助手将为所有TStringList对象定义新的行为。
type
  TStringsHelper = class helper for TStrings
    private
      function GetValue(const Name: string): string;
      procedure SetValue(const Name, Value: string); reintroduce;
    public
      property Values[const Name: string]: string read GetValue write SetValue;
  end;


function TStringsHelper.GetValue(const Name: string): string;
begin
  Result := Self.GetValue(Name);
end;

procedure TStringsHelper.SetValue(const Name, Value: string);
var
  I: Integer;
begin
  I := IndexOfName(Name);
  if I < 0 then I := Add('');
  Put(I, Name + NameValueSeparator + Value);
end;

我认为一个派生类最适合。然后我们通过更改派生类中的 Values 行为可以使 Lazarus 和 Delphi 代码以相同方式工作,并开始使用派生类,如果有人需要 TStrings 的行为,则将其保留为原样。我注意到键的使用也有限制,这取决于 NameValueSeparator 属性。如果您设置 kv['foo=bar']:='baz',则 kv['foo'] 现在是 'bar=baz'。在派生类中,我还可以防止使用具有 NameValueSeparator 的键。 - Allan Ojala
就我个人而言,我不太喜欢helpers:我只用过一次,是因为有些遗留代码无法以其他方式修复。在我看来,派生类通常是更合适的解决方案。我不知道键的限制,但您是否考虑过使用TDictionary?当处理键/值对时,它可能适合您的需求,并且应该比TStringList更快。 - fantaghirocco
是的,我已考虑过 TDictionary,但由于我们使用的 Lazarus 版本不支持该语言特性,所以我们不能使用它。而且,我同意 TString.Values 的性能较差,因为当你添加键值对时,搜索时间会呈线性增长。在我的情况下,性能不是问题,因为我的元素很少。我不知道将 Sorted 属性设置为 true 是否有助于更快地查找键。关于这个问题,Delphi 和 Lazarus 的文档都缺乏说明。 - Allan Ojala

1
这个怎么样?
procedure TMyClass.Set(const Key, Value: String);
var
  i:integer;
begin
  i := FData.IndexOfName(Key);
  if i = -1 then
    FData.Add(Key + '=' + Value)
  else
    FData[i] := Key + '=' + Value;
end;

你可以选择是否设置FData.Sorted:=true;

0

TStringList没有这个选项。它的行为是在值为空('')时删除一个条目。

您可以通过自己实现该行为来解决问题,例如,向您的值添加前缀:

procedure TMyClass.Set(const Key, Value: String);
begin
  FData.Values[Key] := '_' + Value;
end;

但这也意味着,您需要一个getter来再次删除它:

function TMyClass.Get(const Key): String;
begin
  Result := StringReplace(FData.Values[Key], '_', '', []);
end;

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