[delphi-users:4734] TDictionaryの使い方

1,025 views
Skip to first unread message

ストラテジー鈴木

unread,
Aug 30, 2017, 4:53:09 AM8/30/17
to delphi...@freeml.com
いつも参考にさせていただいております。鈴木です。

TDictionaryについて教えてください。

次のようなコード(コンソールプログラム)を書いてみました。
Delphi 10.2 tokyo Update 1を使用しています。

--------------------------
uses
System.SysUtils, System.Generics.Collections;

type
TMyRcd = record
fInt: Integer;
procedure SetInt(Val: Integer);
end;
TMyRcds = class( TDictionary<String,TMyRcd> )
end;

var
rcd: TMyRcd;
rcds: TMyRcds;

{ TMyRcd }
procedure TMyRcd.SetInt(Val: Integer);
begin
fInt := Val;
end;

begin
rcds := TMyRcds.Create();

rcd.fInt := 100;
rcds.Add( 'Key1',rcd );

Writeln( Format( 'Key1:%d',[rcds.Items['Key1'].fInt] ) );

// Key1で参照できるレコード型のフィールドの値を変えたい。
// コンパイルエラー。プロパティの所為?
// rcds.Items['Key1'].fInt := 300;
// ※1
rcds.Items['Key1'].SetInt( 300 );

// ※2 変わらない...
Writeln( Format( 'Key1:%d',[rcds.Items['Key1'].fInt] ) );

rcds.Free;

Readln;
end.
----------------------------


 デバッガで確認したところ、上記の※1では正しく動作しているように思うのですが、※2の行を実行しても「100」が表示されてしまい、変更したはずの「300」が表示されません。
 基本的なTDictionaryの使い方が間違っているのでしょうか。
 何かおわかりでしたら、教えてください。
 よろしくお願いいたします。

鈴木


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
☆CM放映で今話題☆ 顔から足先までぜ~~~んぶ全身脱毛!!
スリムアップ全身脱毛33か所 月額9,500円(税抜)の初回分が無料!!!!
予約がとりやすいから夏までに間に合う♪♪
◆無料カウンセリングでご来店の方!今ならハンド脱毛体験無料実施中◆
http://ad.freeml.com/cgi-bin/sa.cgi?id=qpAId
------------------------------------------------------[freeml byGMO]--

あなたの街のチラシがいつでも無料で見放題!
チラシをクリックしてチラシが拡大されたらポイントゲット♪
まずはかんたん登録♪ -ポイントタウン-
http://www.pointtown.com/ptu/rd.cgi?cid=8912
----------------------------------------------------------------------

umez

unread,
Aug 30, 2017, 7:21:48 AM8/30/17
to delphi...@freeml.com
こんにちは、梅澤@プロキャストです。

値の変更を、

> // rcds.Items['Key1'].fInt := 300;
> // ※1
> rcds.Items['Key1'].SetInt( 300 );

のように1行で行おうとせず、

var
rcd2: TMyRcd


rcd2 := rcds.Items['Key1'];
rcd2.fInt := 300;
rcds.Items['Key1'] := rcd2;

のように書くことで、問題なく変更できました。


TDictionary<TKey,TValue> のItemsプロパティは

property Items[const Key: TKey]: TValue read GetItem write SetItem;
default;

のようになっていますので、
rcds.Items['Key1'].fInt := 300;
のように行うと、コンパイラが、
rcds.Items['Key1']
までを解釈した段階で、代入先のない、一時的なTValue 型を返し、その
一時的なエリアの fInt に 300を代入することになるものと思われます。


ストラテジー鈴木 <delphi...@freeml.com> さんは書きました。<2017/08/30
18:00 >
----------
Tomomi Umezawa um...@procast.co.jp
Tel 03-5764-2371 Fax 03-3763-7775


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
一つでも当てはまる方今すぐ体験!
【ヒゲ剃りが面倒】【青く見えるヒゲ】【剃ってもすぐ生える】
大人気!ヒゲ脱毛1,000円体験コース【MEN’S TBC 】
http://ad.freeml.com/cgi-bin/sa.cgi?id=qpCn2
詳しくは△▲URL▲△を今すぐチェック!

ストラテジー鈴木

unread,
Aug 30, 2017, 9:41:26 AM8/30/17
to delphi...@freeml.com
梅澤さん、こんばんは。

返信ありがとうございます。

| rcds.Items[''Key1'']
| までを解釈した段階で、代入先のない、一時的なTValue 型を返し、その
| 一時的なエリアの fInt に 300を代入することになるものと思われます。

なるほどです。
実体では無く、そのコピーに対して操作していたわけですね。

項目数が多いので、なんとか簡潔に書ける方法は無いのかと思っていたのですが。
レコード型では無く、クラスにすれば大丈夫そうな気もしてきましたが、メモリの解放処理を考えるとどうしようか迷います。

とりあえずは教えていただいた方法で対応してみます。

ありがとうございました。

鈴木


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
夏着前!薄着になる“今”、脱毛しましょう!!
【エステティックTBC 】選べる脱毛 1,000円(税込)
http://ad.freeml.com/cgi-bin/sa.cgi?id=qpDBm
予約日当日、すぐに脱毛できる♪
詳しくは△▲URL▲△を今すぐチェック!

c-yan

unread,
Aug 30, 2017, 2:07:56 PM8/30/17
to delphi...@freeml.com
c-yan です.

> のように行うと、コンパイラが、
> rcds.Items['Key1']
> までを解釈した段階で、代入先のない、一時的なTValue 型を返し、その
> 一時的なエリアの fInt に 300を代入することになるものと思われます。

なんか説明がおかしい気がします.
Record は参照型ではなく値型なので、参照渡しとかをしない場合は値そのものを渡すのでメモリ上の領域が違うのは当たり前です.
挙動的には Integer とかと同等です.
Integer代入する度に一時的ななんかを作ってんだなんていう人はいないと思います.

なんで今回のなんで変わらないんだという話は

procedure Proc1(I: Integer);
begin
I := 10;
end;

var
I: Integer;
begin
I := 0;
Proc1(I);
Writeln(I);
end.

で、10 が表示されないよ、なんでと言っているのと似たような話です.
# ご存知だとは思いますが、procedure Proc1(var I: Integer); であれば変わります.

また、コンパイラが解釈した段階でという表現もよくわかりません.
rcds内部に保存されたTValueと別のメモリストレージ(というのはメモリなのかレジスタなのかが分からんため)に値を保存する機械語をコンパイラが吐きますが、保存先がレジスタでなければそのアドレスが決まるのはコンパイル時ではなく実行時に決まるかと思います.
結論的には更新されないのは単に値を Dictionary に代入していないのが問題で、それ以上でもそれ以下でもありません.
例えば以下の様なコードだったら更新されますし、また TValue が値型ではなく Class みたいな参照型であれば、値として参照が帰ってくるので、参照を通してrcds内部に保存されたメモリにアクセスして、Dictionary に代入をせずに更新がかけれるかと思います.

uses
System.SysUtils, System.Generics.Collections;

type
TMyRcd = record
fInt: Integer;
function UpdateInt(Val: Integer): TMyRcd;
end;
TMyRcds = class( TDictionary<String,TMyRcd> )
end;

var
rcd: TMyRcd;
rcds: TMyRcds;

{ TMyRcd }
function TMyRcd.SetInt(Val: Integer): TMyRcd;
begin
Self.fInt := Val;
Result := Self;
end;

begin
rcds := TMyRcds.Create();

rcd.fInt := 100;
rcds.Add( 'Key1',rcd );

Writeln( Format( 'Key1:%d',[rcds.Items['Key1'].fInt] ) );

rcds.Items['Key1'] := rcds.Items['Key1'].SetInt( 300 );
--
c-yan
http://www.cyanet.jp/


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
EPARKママこえ上野動物図鑑で
入園券が通常よりもお得に購入できるようになりました。
土日祝日、夏休みはチケット売り場も混雑が予想されるので
事前に購入すればスムーズに入園できちゃいます♪
http://ad.freeml.com/cgi-bin/sa.cgi?id=qpEF9

ストラテジー鈴木

unread,
Aug 30, 2017, 10:41:12 PM8/30/17
to delphi...@freeml.com
こんにちは、c-yanさん。

解説ありがとうございます。

| Record は参照型ではなく値型なので、参照渡しとかをしない場合は値そのものを渡す

 梅澤さんのヒントでかなり助かったのですが、「Recordは値型である」と例示していただいた「変わらない理由のサンプルコード」で、大分すっきりしました。

 いま手元に開発環境が無いので、あとでfunction UpdateInt(Val: Integer): TMyRcd;も試してみたいとおもいます。

鈴木


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
☆CM放映で今話題☆ 顔から足先までぜ~~~んぶ全身脱毛!!
スリムアップ全身脱毛33か所 月額9,500円(税抜)の初回分が無料!!!!
予約がとりやすいから夏までに間に合う♪♪
◆無料カウンセリングでご来店の方!今ならハンド脱毛体験無料実施中◆
http://ad.freeml.com/cgi-bin/sa.cgi?id=qpHB8

中村拓男(自宅)

unread,
Aug 30, 2017, 11:08:30 PM8/30/17
to delphi-users
中村です。レコード型をコレクションに使うのは
あまりお勧めできないです。フィールド単位に
変更できないですよね。

クラス型を使い、TObjectDictionaryのownershipを
活用することをお勧めします。これは辞書の中に
残ったオブジェクトを辞書が責任を持って
自身が破棄されるときに破棄する指定です。


2017/08/31 午前11:41 "ストラテジー鈴木" <delphi...@freeml.com>:

ストラテジー鈴木

unread,
Aug 31, 2017, 5:34:44 AM8/31/17
to delphi...@freeml.com
中村さん、こんにちは。鈴木です。

| あまりお勧めできないです。フィールド単位に
| 変更できないですよね。

 そうなんです。

| クラス型を使い、TObjectDictionaryのownershipを
| 活用することをお勧めします。これは辞書の中に
| 残ったオブジェクトを辞書が責任を持って
| 自身が破棄されるときに破棄する指定です。

 情報ありがとうございます。
 デルファイルのヘルプにも「TDictionary から派生した TObjectDictionary クラスは、辞書エントリから削除されたオブジェクトを自動的に解放するメカニズムを提供します。 」と書いてありました。

 後々のことを考えてクラス型に変更することを検討してみます。



鈴木


MLホームページ: http://www.freeml.com/delphi-users

----------------------------------------------------------------------
一つでも当てはまる方今すぐ体験!
【ヒゲ剃りが面倒】【青く見えるヒゲ】【剃ってもすぐ生える】
大人気!ヒゲ脱毛1,000円体験コース【MEN’S TBC 】
http://ad.freeml.com/cgi-bin/sa.cgi?id=qpL6e
詳しくは△▲URL▲△を今すぐチェック!
Reply all
Reply to author
Forward
0 new messages