2010-10-23 9 views
5

Je voudrais avoir un dictionnaire qui retourne une valeur par défaut lorsque la clé de recherche est introuvable. Lecture à partir de la documentation:Delphi Generics> Dictionnaire avec la valeur par défaut

Generics.Collections.Tdictionary [...] Cette classe fournit un mappage et un contenu initial.

1 - Comment? Y at-il un moyen de le faire ala Python: {1: 'un'; 2: 'deux'}? [...] TryGetValue renvoie true si la clé donnée figure dans le dictionnaire et fournit sa valeur dans Value. Sinon, il renvoie false et Value est défini sur le type de valeur par défaut de Tvalue.

2 - Comment puis-je définir cette valeur par défaut? Je ne peux pas trouver un constructeur (peut-être que je viens de chercher au mauvais endroit Je m'attends à quelque chose comme "constructeur Create (DefaultValue: TValue);")

Alors j'essaie de mettre en œuvre mon propre (peut être n'est pas nécessaire Voir ci-dessus.): (! commentaires et suggestions sont les bienvenus)

Le code est:

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; 

et les tests sont:

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. 

C'est loin d'être être complet. J'aimerais que l'opérateur de l'indexeur travaille également (voir le dernier test). C'est possible? Qu'est-ce qui devrait également être mis en œuvre?

Est-ce que cette implémentation fuit M_DefaultValue (je suis nouveau à Delphi). Je ne peux pas faire quelque chose M_DefaultValue.Free dans le destructeur (n'est pas si flexible en raison de la contrainte constructeur) Que peut-on faire ici?

Merci à l'avance,

Francis

+0

Vous avez soulevé une très bonne question, puisque actuellement, en Delphi, nous devons faire deux recherches de dictionnaire. La même chose est vraie à propos de PHP. –

Répondre

1

Avant d'écrire tout ce code vous-même, vous pouvez regarder les classes génériques dans le DeHL library.

Il soutient ceci:

Type de concept de support qui définit un ensemble de par défaut "classes de soutien" pour chaque type Delphi intégré (utilisés comme par défaut dans les collections). Les classes de support "type " personnalisées peuvent être enregistrées pour vos types de données personnalisés.

--jeroen

+0

Je m'attends à quelque chose de différent: les comparateurs par défaut et autres, pas une valeur par défaut renvoyée par un dictionnaire en cas de "clé non présente". –

+0

Votre réponse n'est pas si amicale. L'idée principale de l'auteur était d'éviter deux recherches: la première recherche ne vérifie pas si la valeur existe et la deuxième recherche renvoie la valeur. Si vous auriez fourni une réponse simple et claire, avec un exemple de code, cela aurait été utile, par exemple sur l'utilisation de TryGetValue et que chaque type a une valeur par défaut qui ne peut pas être changée, par exemple, Integer a 0, string a '', etc. –

+0

@MaximMasiutin en tant qu'utilisateur francis n'a jamais répondu (dernière fois actif en 2010) et a soulevé une poignée de questions dans une question SO, il est difficile de comprendre ce que son stumbleblock est. Donc - étant donné l'état du code dans la question - je supposais que l'écriture d'un bon code de bibliothèque stable était le problème majeur et direct à la bibliothèque - alors en cours - DeHL. De nos jours je dirigerais à http://spring4d.org –

0

Le principal problème est GetItem n'est pas virtuel dans TDictionary <>. Ceci peut être résolu en ajoutant simplement

property Items[const Key: K]: V read GetValueOf write SetItem; default; 

à votre classe. Btw, les destructeurs doivent être redéfinis, pas réintroduits, donc lorsque vous les stockez dans une variable définie comme classe de hiérarchie supérieure, elle appelle le bon destructeur. C'est juste une bonne pratique, mais dans ce cas précis Items [] ne fonctionnerait pas comme vous le voulez si vous l'avez fait.

Que Dieu vous bénisse.

Questions connexes