- PR -

Control.ControlCollection.Add メソッドについて

投稿者投稿内容
さかもと
ぬし
会議室デビュー日: 2004/05/14
投稿数: 586
投稿日時: 2006-04-19 15:55
いつもお世話になります。
さかもとと申します。
VS2005(VB2005:Winアプリ)
framework2.0

フォームA上に配置されてグループボックス、
さらにそのグループボックス内に配置された
コントロールを別のグループボックスに
追加する方法を検討しています。

現在テストとして、グループボックス上に
9つのコントロールを配置しています。

コード:
Dim myGpb As New GroupBox
For Each hControl As Control In GroupBoxA.Controls
    myGpb.Controls.Add(hControl)
Next hControl



上記のように記述して、myGpbに9つのコントロール
がセットされるかと思いましたが、実際には
5つのコントロールしかセットされません。

それぞれのグループボックス内のコントロールの
数をループ内で見たところ

GroupBoxA.Controls.Count(左) - myGpb.Controls.Count(右)

8-1
7-2
6-3
5-4
4-5

と左が4になった時点でループを抜けてしまいます。
Controls.Addメソッドはドキュメントによると
「Control が既に他のコントロールの子コントロールである場合は、
別のコントロールに追加される前にそのコントロールから削除されます。」
とのことなので最終的にGroupBoxAのコントロールの数が
0になるまで繰り返されるのかと思っていましたが、
コントロールの数を増減させて試したところ、
上記の例でいうと(左)の数が(右)の数より少なく
なった時点でループを抜けているようです。

記述方法でおかしなところがあればご指摘頂ければと
思います。

宜しくお願いいたします。
まどか
ぬし
会議室デビュー日: 2005/09/06
投稿数: 372
お住まい・勤務地: ますのすし管区
投稿日時: 2006-04-19 16:14
引用:

コード:

Dim myGpb As New GroupBox
For Each hControl As Control In GroupBoxA.Controls
myGpb.Controls.Add(hControl)
Next hControl


と左が4になった時点でループを抜けてしまいます。


GroupBoxA.Controlsを列挙している最中に数が変動するというのを気持ち悪く感じませんか?
#って、それが原因だよなぁ?。。。ちょっと不安

コード:

For i As Integer = 0 to GroupBoxA.Controls.Count - 1
Dim hControl = GroupBoxA.Controls.Item(0)
'GroupBoxA.Controls.RemoveAt(0)
myGpb.Controls.Add(hControl)
Next


でどうでしょうか。

[ メッセージ編集済み 編集者: まどか 編集日時 2006-04-19 16:16 ]
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2006-04-19 16:27
引用:

GroupBoxA.Controlsを列挙している最中に数が変動するというのを気持ち悪く感じませんか?
#って、それが原因だよなぁ?。。。ちょっと不安


多分そうですね。
引用:

コントロールの数を増減させて試したところ、
上記の例でいうと(左)の数が(右)の数より少なく
なった時点でループを抜けているようです。


というより、半分になった時点で、という方が正しいでしょうね。

という事で、For Each で回しているコレクションを For Each 内で削除すると意図しない動きになってしまいます。

#でも、明示的に削除のコードがあるわけじゃあないから、難しいですね、コレ。



[ メッセージ編集済み 編集者: 囚人 編集日時 2006-04-19 16:29 ]
さかもと
ぬし
会議室デビュー日: 2004/05/14
投稿数: 586
投稿日時: 2006-04-19 16:29
さかもとです。
まどか様、ご返答ありがとうございます。

コード:
For i As Integer = 0 to GroupBoxA.Controls.Count - 1
    Dim hControl = GroupBoxA.Controls.Item(0)
    'GroupBoxA.Controls.RemoveAt(0)
    myGpb.Controls.Add(hControl)
Next


でうまくいきました。
ただ、
>#って、それが原因だよなぁ?。。。ちょっと不安
おっしゃるように、それが原因かどうかが分かりません・・・。

コード:
        myGpb.Controls.Add(CheckBox1)
        myGpb.Controls.Add(CheckBox2)
        myGpb.Controls.Add(CheckBox3)
        myGpb.Controls.Add(CheckBox4)
        myGpb.Controls.Add(CheckBox5)
        myGpb.Controls.Add(CheckBox6)
        myGpb.Controls.Add(CheckBox7)
        myGpb.Controls.Add(CheckBox8)
        myGpb.Controls.Add(CheckBox9)


この場合はもちろんうまくいきますが、最初に私が試した
コントロールの列挙中での処理の場合だけうまくいかない
ような・・・。

.Netframework1.1で最初の例を試したところ
(以下は最初の書き込みの(左)-(右)です)
8-1
7-2
6-3
5-4
4-5
4-5
4-5
4-5
4-5
と9回ループした後に抜けています。
ただし、コントロールの数は4つ5つのままで2.0での結果と
変わりませんでした。

方法としてはやはりよろしくないのでしょうか・・・。
さかもと
ぬし
会議室デビュー日: 2004/05/14
投稿数: 586
投稿日時: 2006-04-19 16:41
囚人様
ご返答ありがとうございます。

>、For Each で回しているコレクションを For Each 内で削除すると意図しない動きになってしまいます。

なるほど、そういうことなんですね・・・。
理論上は綺麗に行くはずじゃないかと思い込んでいましたが、
言われてみればおっしゃる通りかと・・・。

まどか様のコードで実装させて頂きます。
勉強になりました、ありがとうございました。
まどか
ぬし
会議室デビュー日: 2005/09/06
投稿数: 372
お住まい・勤務地: ますのすし管区
投稿日時: 2006-04-19 16:44
引用:

方法としてはやはりよろしくないのでしょうか・・・。


はい。
方法というより、先に書いたように依存しているオブジェクトが変動することがよくありません。
#してはいけないと言ったほうがよいかもしれません。

For Each hControl As Control In GroupBoxA.Controls
この場合、毎回GroupBoxA.Controls.GetEnumeratorが呼び出されます。
つまり毎回GroupBoxA.Controlsの状態を参照してそれに依存しているわけです。

#実際の挙動ではありません
たとえば、GetEnumeratorが呼び出し側に返した数を管理しているとします。
すると返す際に現在の総数-返した数が残りとなります。
返した数は+1、残りは−1かつ呼び出し側で削除した分−1となり
囚人さんがおっしゃるように元の総数の半分で終了することになります。

対して
For i As Integer = 0 to GroupBoxA.Controls.Count - 1
この場合、For文でGroupBoxA.Controls.Countが評価されて値に置き換わります。
したがって構文上ループ回数が変動することはありません。
まどか
ぬし
会議室デビュー日: 2005/09/06
投稿数: 372
お住まい・勤務地: ますのすし管区
投稿日時: 2006-04-19 16:52
ちなみに、スレッド処理では考慮していないと
今回のように変動するような実装が無くても似たようなことが起こります。
#ヘルプ「マネージスレッド処理の実施」内の「競合状態」を参照
ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.ja/dv_fxadvance/html/e51988e7-7f4b-4646-a06d-1416cee8d557.htm
同一オブジェクトをいろんなところから同時に操作するという場合です。

[ メッセージ編集済み 編集者: まどか 編集日時 2006-04-19 16:53 ]
さかもと
ぬし
会議室デビュー日: 2004/05/14
投稿数: 586
投稿日時: 2006-04-19 16:54
さかもとです。
まどか様、ご丁寧にありがとうございます。

>この場合、毎回GroupBoxA.Controls.GetEnumeratorが呼び出されます。
>つまり毎回GroupBoxA.Controlsの状態を参照してそれに依存しているわけです。

なるほど、内部ではこのようになっているわけですね。
「毎回」という部分に関して勘違いしていました。
「ある時点での状態」を保持してそれを都度見に行っているわけでは
ないと・・・。
頭のモヤモヤがすっきりしました(笑)

お時間頂戴しまして、申し訳ありませんでした。
ありがとうございました。

スキルアップ/キャリアアップ(JOB@IT)