TDictionary
Delphi には TDictionary というクラスがあります。
いわゆる連想配列(Key-Value ペアによるデータ構造)です。
var Dic := TDictinary<String, Integer>.Create;
try
Dic.Add('FooKey', 100); // 'FooKey' と値 100 を登録
Dic['FooKey'] := 42; // 'FooKey' に 42 を代入
var V := Dic['FooKey']; // 'FooKey' の値を取り出せる
Writeln(V); // 42 が表示される
finally
Dic.Free;
end;
TDictionary の面倒なところ
TDictionary は存在しないキーの値を取り出したり設定すると例外が発生します。
var Dic := TDictinary<String, Integer>.Create;
try
Dic['BarKey'] := 42; // 存在しないキー 'BarKey' にアクセスしたため例外が発生する
finally
Dic.Free;
end;
そのため ContainsKey でキーが存在するか調べたり AddOrSetValue メソッドを使うなどしてキーを登録しなければいけません。
var Dic := TDictinary<String, Integer>.Create;
try
Dic.AddOrSetValue('BarKey', 42);
finally
Dic.Free;
end;
値の取得時と設定時ではアクセス方法が変わって気持ちが悪いわけです。
ところで C# の Dictionary は存在しないキーにアクセスすると自動的にキーが生成されます。
var Dic = new Dictinary<String, Integer>();
Dic["BarKey"] = 42; // 自動的に 'BarKey' が生成される
Delphi でも C# みたいにアクセスしたいよお
つくったよ
ちょっとした改造だけで同じ事ができます。
短いので↓に全部載せました。
コードを表示
(*
* C# の Dictionary の様にキーが存在しない場合は自動的に追加される Dictionary
*
* PLATFORMS
* All
*
* ENVIRONMENT
* Delphi 11
*
* LICENSE
* Copyright (c) 2022 HOSOKAWA Jun
* Released under the MIT license
* http://opensource.org/licenses/mit-license.php
*
* HISTORY
* 2022/05/12 Version 1.0.0
*
* Programmed by HOSOKAWA Jun (twitter: @pik)
*)
unit PK.Utils.CSDictionary;
interface
uses
System.Generics.Collections;
type
TCSDictionary<K, V> = class(TDictionary<K, V>)
private
function GetItem(const AKey: K): V;
procedure SetItem(const AKey: K; const AValue: V);
public
property Items read GetItem write SetItem; default;
end;
implementation
{ TCSDictionary<K, V> }
function TCSDictionary<K, V>.GetItem(const AKey: K): V;
begin
if ContainsKey(AKey) then
Result := inherited Items[AKey]
else
begin
Result := Default(V);
Add(AKey, Result);
end;
end;
procedure TCSDictionary<K, V>.SetItem(const AKey: K; const AValue: V);
begin
AddOrSetValue(AKey, AValue);
end;
end.
解説
コード中の Property Override についてだけ解説します。
親に定義してある property を override する方法が Property Override です。
↓こんな風に property の型を書かないで宣言した場合、親で定義されている property の可視性や reader / writer 等を変更できます(reader / writer はどちらか片方でもOK)。
property Items read GetItem write SetItem; default;
property の機能は実質的に reader / writer で定義されるので、ここを変えれば自由に機能を変更出来るというわけです。
注意したいのは property が親クラスで default 宣言されていた場合、override 側にも default 宣言が必要になるところです。
もしも default を指定しないと親クラスの default プロパティが呼ばれてしまいます。
使い方
var Dic := TCSDictionary<String, String>.Create;
try
// 存在しない Key 'Foo' に代入できる
Dic['Foo'] := 'Bar';
// 存在しない Key 'Baz' を取得できる(Default 値が返る)
WriteLn(Dic['Baz']);
finally
Dic.Free;
end;
便利~!
最後に
参照はまだしも設定はできてもいいのにな~