- - PR -
VB.NETでhashtableに入れた構造体の再代入でエラー
1
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2004-07-27 09:41
初めて質問します。よろしくお願いします。
VB.NETは今だ2002です。 やりたいことはhashtableを使ってkeyごとにtest構造体のa,bに引数のa,bを加算し、 加算し終わったらkeyごとにtest構造体のcにa/bを代入したいのです。 ============================================================================== '集計クラス Public Structure Test Public a As Double Public b As Double Public c As Double End Structure Public Class Calc Private hs As New Hashtable() 't.keyごとにt.a,t.bを加算する Public Sub sum(ByVal key As String, ByVal a As Double, ByVal b As Double) If Not hs.ContainsKey(key) Then Dim t As Test t.a = a t.b = b hs.Add(key, t) Else Dim t As Test = CType(hs(key), Test) t.a += a t.b += b hs(key) = t End If End Sub 't.c=t.a/t.b Public Sub div() Dim key As String For Each key In hs.Keys Dim t As Test = CType(hs(key), Test) t.c = t.a / t.b hs(key) = t Next End Sub End Class ============================================================================== '呼び出し Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim clc As New Calc() clc.sum("A", 1.0, 2.0) clc.sum("A", 2.0, 3.0) clc.sum("B", 4.0, 2.0) clc.sum("B", 1.0, 5.0) clc.sum("A", 1.0, 2.0) clc.div() '<- ここでエラー End Sub ============================================================================== Calcクラスのsumメソッドは正常に動きますが、divメソッドを呼ぶと 'System.InvalidOperationException' のハンドルされていない例外が mscorlib.dll で 発生しました。 追加情報 : コレクションが変更されました。列挙操作は実行されない可能性がありま す。 というエラーが出てしまいます。sumメソッドのときは構造体を再代入しても問題なかっ たのに、divメソッドではなぜエラーになるのか分かりません。for eachで回しているせ いでしょうか?この場合どのようにコードを書けばいいのでしょうか? みなさまの知恵をお貸しください。 | ||||||||||||
|
投稿日時: 2004-07-27 10:17
こんにちは。
うすうすは感づいていらっしゃるようで。
列挙インターフェース(IEnumerator)の性質で、 要素をいじると以降の順序アクセス(たぶん内部的にMoveNext) で怒られるみたいですね。 さて、問題の 「hs(key) = t」ですが、 実はこれを無くしても期待する動作を行うような気がします。(未確認) 一連のtの代入はオブジェクト参照なので書き戻しをするまでもなく、 ハッシュテーブルのコンテナ内部のTestオブジェクトのメンバ 「a.b.c」にアクセスがすんでいませんか? それとも構造体の代入はコピーなんでしたっけ? _________________ | ||||||||||||
|
投稿日時: 2004-07-27 10:18
div()内でFor Eachで繰り返していますよね。
で、For Each内でHashTableが書き換えられているために、内部で呼び出されているMoveNext()メソッドが無効状態になっているわけです。 MSDNライブラリのIEnumerator.MoveNext()メソッドの説明に以下のように書いてあります。 http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpref/html/frlrfsystemcollectionsienumeratorclassmovenexttopic.asp コレクションが変更されない限り、列挙子は有効なままです。要素の追加、変更、削除などの変更がコレクションに対して実行されると、列挙子は回復不可能な無効状態になり、次に MoveNext または Reset を呼び出すと、 InvalidOperationException がスローされます。 >あみゅせるさん >それとも構造体の代入はコピーなんでしたっけ? そうですね。ボックス化されて参照型になってはいますが、 CType(hs(key), Test) とボックス化を解除した時点で、その参照型のインスタンスとは別の構造体がメモリ上に作られます。 ソースを見る限り、For Eachで回しても要素のキーや数は変わらなそうですから、Keysプロパティのコレクションをコピーして、それからそのコピーしたコレクションでFor Eachってのはどうです? [ メッセージ編集済み 編集者: 一郎 編集日時 2004-07-27 10:27 ] | ||||||||||||
|
投稿日時: 2004-07-27 10:54
あみゅせるさま、一郎さま、早速ご返答ありがとうございます。
構造体の代入はコピーだったので、Test構造体をクラスにしてdivメソッドで hs(key) = t をはずすときちんとできました。 そもそも構造体にしたのはいちいち生成しなくてよいこと、構造体の方が メモリ消費量が少ないということからだったのです。 参考記事:プロフェッショナルVB.NETプログラミング 第3回 構造体の宣言とその効能 http://www.atmarkit.co.jp/fdotnet/vb6tonet/vb6tonet03/vb6tonet03_03.html 実際keyの数は非常に多くなりそうなので、構造体のままで対処を考えたいと思って います。 >一郎さま >ソースを見る限り、For Eachで回しても要素のキーや数は変わらなそうですから、Keys >プロパティのコレクションをコピーして、それからそのコピーしたコレクションでFor >Eachってのはどうです? 具体的にどうやればいいのか分かりませんでした。何かサンプルを教えていただけると 助かります。 | ||||||||||||
|
投稿日時: 2004-07-27 11:26
なるほど、そういう理由で構造体ですか。 ボックス化とボックス化解除について意識をされるとよろしいかと思います。 http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/csref/html/vclrfboxingunboxingpg.asp 今回の場合はHashTableに入れる段階でボックス化されていますので、HashTableに入れた構造体と同じ数の参照型のインスタンスが生成されています。(残念ながら) そして値を取り出すときにはまた同じ数の構造体が生成されます。 現在のソースでは、構造体の悪い部分(HashTableに入っている構造体の値だけを書き換えられない)とクラスの悪い部分(インスタンスの生成の影響)の両方の影響を受けているということですね。 クラスのほうがパフォーマンスが良いのではないでしょうか。 | ||||||||||||
|
投稿日時: 2004-07-27 11:40
一郎さま
>今回の場合はHashTableに入れる段階でボックス化されていますので、HashTableに入れ >た構造体と同じ数の参照型のインスタンスが生成されています。(残念ながら) >そして値を取り出すときにはまた同じ数の構造体が生成されます。 つまりメモリ消費を気にして構造体にした意味がないということですね。 むしろクラスの方がコードもすっきりするので、もう一度検討します。 構造体とクラスについては中途半端な知識しかなかったので、ボックス化については とても参考になりました。ありがとうございました。 | ||||||||||||
|
投稿日時: 2004-07-27 14:04
こんにちは。
一郎さん。ありがとうございます。
う〜む そうですよね。 「何でも積めるコンテナ」はホントに積むだけだった...
これが通らないのには一瞬固まりました。 よく理解していないことが確認できました。 piromiさん
今回は適用しにくかったり、構造体も一気に大量確保するわけではないので クラスで実装したほうがいいと思います。 でも、これらを検討したことが大変に意味があると思います。 私も、勉強させていただきました _________________ |
1