Delphi 泛型 > 带有默认值的字典

5
我希望有一个返回默认值的字典,在搜索键未找到时能够使用。根据文档所述:
Generics.Collections.Tdictionary […] 此类提供映射 […] 和初始内容。
1 - 如何实现?是否可以像 Python 一样:{1: ‘one’, 2:’two’}?
Generics.Collections.TDictionary.TryGetValue [...] 如果给定键在字典中,则TryGetValue 返回 true,并在 Value 中提供其值。否则,它返回 false 并将 Value 设置为 Tvalue 的默认值类型。
2 - 我该如何设定此默认值?我无法找到构造函数 (也许我只是在错误的地方搜索。我期望会有像 “constructor Create(DefaultValue: TValue);” 这样的函数)
因此我尝试自己实现(也许不必要。见上):
以下是代码 (欢迎评论和建议!):
unit Util;

interface

uses
    Generics.collections;

type

    //
    // Dictionary with default response
    //
    TDefaultDictonary<K, V> = class(TObjectDictionary<K, V>)
    private
        M_DefaultValue : V;

    public
        constructor Create(Defaultvalue : V);
        destructor Destroy; reintroduce;
        function GetDefaultValue : V;
        function TryGetValue(const Key: K; out Value: V): Boolean;
        function GetValueOf(const Key: K) : V;
    end;

implementation

//
// Contructor and destructor
//
constructor TDefaultDictonary<K, V>.Create(Defaultvalue : V);
begin
    inherited Create;

    M_DefaultValue := Defaultvalue;
end;

destructor TDefaultDictonary<K, V>.Destroy;
begin
    inherited Destroy;
end;

//
// Get the default Value
//
function TDefaultDictonary<K, V>.GetDefaultValue : V;
begin
    Result := M_DefaultValue;
end;


//
// Try to get a value from the dictionary for the given key.
//
// If the value is found then "Value" holds it and the function returns true.
// If the value is not found then "Value" holds the default value and the
// function returns false.
//
function TDefaultDictonary<K, V>.TryGetValue(const Key: K; out Value: V): Boolean;
var
    IsKeyFound : boolean;
    DictVal : V;

begin
    IsKeyFound := inherited TryGetValue(Key, DictVal);
    if not IsKeyFound then begin
        DictVal := M_DefaultValue;
    end;

    // Outputs:
    Value := DictVal;
    Result := IsKeyFound;
end;


//
// Get a value from the dictionary for the given key.
//
// If the value is found then the function returns it.
// If the value is not found the function returns the default value.
//
function TDefaultDictonary<K, V>.GetValueOf(const Key: K) : V;
var
    DictVal : V;

begin
    TryGetValue(Key, DictVal);

    Result := DictVal;
end;

测试如下:

unit Test_Utils;
{
    Test the TDefaultDictionary functionality
}

interface

uses
    Sysutils, Math, TestFramework, Util;

type

    TestUtil = class(TTestCase)

    public
        procedure SetUp; override;
        procedure TearDown; override;

    published
        procedure TestDefaultDictionaryGetDefaultResponse;
        procedure TestDefaultDictionaryExistingKey;
        procedure TestDefaultDictionaryNotExistingKey;

    end;


implementation


procedure TestUtil.SetUp;
begin
end;

procedure TestUtil.TearDown;
begin
end;


procedure TestUtil.TestDefaultDictionaryGetDefaultResponse;
var
    dd : TDefaultDictonary<integer, string>;

begin
    dd := TDefaultDictonary<integer, string>.Create('Default response');
    checkEquals('Default response', dd.GetDefaultValue);

    dd.Free;
end;

procedure TestUtil.TestDefaultDictionaryExistingKey;
var
    dd : TDefaultDictonary<integer, string>;
    outVal : string;
    isKeyFound : boolean;

begin
    dd := TDefaultDictonary<integer, string>.Create('Default response');
    dd.Add(1, 'My one');

    checkEquals(1, dd.Count,
        'One element as count');

    isKeyFound := dd.TryGetValue(1, outVal);
    check(isKeyFound,
        'Key not found by TryGetValue');

    checkEquals('My one', outVal,
        'Value given by TryGetValue');  

    checkEquals('My one', dd[1],
        'Value given by indexing as array');

    dd.Free;
end;


procedure TestUtil.TestDefaultDictionaryNotExistingKey;
var
    dd : TDefaultDictonary<integer, string>;
    outVal : string;
    isKeyFound : boolean;

begin
    dd := TDefaultDictonary<integer, string>.Create('Default response');
    dd.Add(1, 'one');

    isKeyFound := dd.TryGetValue(2, outVal);
    check(not isKeyFound,
        'Key should not be found by TryGetValue');

    checkEquals('Default response', outVal,
        'Default Value given by TryGetValue');

    checkEquals('Default response', dd.GetValueOf(2),
        'Default Value given by indexing as array');

    //
    // It is possible to oveload the indexer operator?
    // Please review or delete me !
    //
    //checkEquals('Default response', dd[2],
    //        'Value given by indexing as array');
    //

    dd.Free;
end;


initialization
    RegisterTest(TestUtil.Suite);
end.

这远远没有完成。我也希望索引器运算符能够工作(请参见最后一个测试)。这是可能的吗?还需要实现什么?

这个实现是否泄漏了M_DefaultValue(我对Delphi不熟悉)。我不能在析构函数中做M_DefaultValue.Free(由于构造函数的限制,这并不太灵活)。应该怎么办?

提前致谢,

Francis


你提出了一个非常好的问题,因为目前在Delphi中,我们需要进行两次字典查找。PHP也是如此。 - Maxim Masiutin
2个回答

1
在你自己编写所有这些代码之前,你可能想看看DeHL库中的通用类。
它支持以下内容:

类型支持概念,为每个内置的Delphi类型定义了一组默认的“支持类”(在集合中用作默认值)。可以为您的自定义数据类型注册自定义“类型支持”类。

--jeroen

我认为这是两回事:默认比较器之类的东西,而不是在字典中“键不存在”时返回的默认值。 - Barry Kelly
你的回复不太友好。作者的问题主要是想避免两次查找:第一次查找是为了检查值是否存在,第二次查找是为了实际返回该值。如果你能提供一个清晰明了的回复,并附上代码示例,那将会很有用。例如如何使用TryGetValue以及每种类型都有一个不能更改的默认值,例如Integer为0,string为''等。 - Maxim Masiutin
由于用户Francis从未回应(上次活跃时间为2010年)并在一个SO问题中提出了一些问题,因此很难理解他的障碍是什么。因此 - 鉴于问题中代码的状态 - 我假设编写良好稳定的库代码是主要问题,并直接使用当时的DeHL库。现在我会引导您前往http://spring4d.org。 - Jeroen Wiert Pluimers

0
主要问题是TDictionary<>中的GetItem不是虚函数。 这可以通过添加来解决。
 property Items[const Key: K]: V read GetValueOf write SetItem; default;

回到你的类中。

顺便说一下,析构函数应该被重写,而不是重新引入,这样当你将它存储在定义为更高层次类的变量中时,它会调用正确的析构函数。这只是最佳实践,但在这种特定情况下,如果你这样做,Items[] 将无法按照你想要的方式工作。

愿上帝保佑你。


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