DOBON.NET DOBON.NETプログラミング掲示板過去ログ

コントロール配列の外部操作

環境/言語:[C#2008]
分類:[.NET]

2009/10/06(Tue) 22:06:03 編集(投稿者)
2009/10/06(Tue) 22:03:36 編集(投稿者)

ここでは、初めて書き込みいたします。
皆さん、よろしくお願いいたします。

早速ですが、Formコントロールについて質問があります。
よろしければ、ご教授頂けると幸いです。


>>>サンプルソース

//form1.cs 
/*
Form1には、3個のテキストボックスと
2個のボタンを貼り付けています。
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
TextBox[] txts = { this.textBox1, this.textBox2, this.textBox3 };

Test objTest = new Test();
objTest.read(txts);
}

private void button2_Click(object sender, EventArgs e)
{
TextBox[] txts = { this.textBox1, this.textBox2, this.textBox3 };

Test objTest = new Test();
objTest.write(txts);
}
}
}


//test.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
class Test
{
/// <summary>
/// textBoxの読み込み
/// </summary>
/// <param name="txts">テキストボックスの配列</param>
public void read(System.Windows.Forms.TextBox[] txts)
{
for (int i = 0; i < txts.Length; i++)
{
Console.Write(txts[i].Text);
}
}

/// <summary>
/// textBoxの書き込み
/// </summary>
/// <param name="txts">テキストボックスの配列</param>
public void write(System.Windows.Forms.TextBox[] txts)
{
for (int i = 0; i < txts.Length; i++)
{
txts[i].Text = "書き換え";
}
}
}
}


質問:
サンプルにあるtextBox1から3は、Modifiersプロパティをprivate設定しています。
Modifiersプロパティは、privateで設定されている際、外部から参照できないという意味で捉えていました。
しかし、エラーが出ずに、テキストボックスを処理できるため理由がわかりません。

■余談
Formクラスにコードが集中しており他のクラスに処理をわけたくて、
短いサンプルコードを書きました。
最初は、コンパイル時にエラーが出ると思っていました。

よろしければ、ご回答頂けると幸いです。
以上、よろしくお願いいたします。
Modifiersプロパティって、実際は Designer.cs 側のクラス宣言で

partial class Form1
{    
    ・・・・・・
    private System.Windows.Forms.TextBox textBox1;
    private System.Windows.Forms.TextBox textBox2;
    private System.Windows.Forms.TextBox textBox3;
}

とアクセス指定子を宣言しているだけなんですが、
この場合 private なので外部からは

Form1 frm = new Form1();
frm.textBox3.Text = "Hello World!"; ← ここでビルドエラー!!

と呼び出せなくなります。

ただし提示のソースの場合、Form1 クラスのメソッド内で
プライベートメンバを配列にして、クラス Test のメソッドに渡しているわけですから
クラス Test からパラメータ変数介して Form1 の TextBox にアクセスできるのは当然ですよね。
2009/10/07(Wed) 01:11:44 編集(投稿者)

ひらぽんさん、ご回答ありがとうございます。
Modifiersプロパティは、特別なことをしていると思っていました。
ただ、アクセス修飾子をつけているだけなんですね。
動かして納得しました。

そう考えると、配列でFormコントロールを他クラスのメソッドに渡して使うのは、場合によって、いいんでしょうか?
(他クラスのメソッドに渡したコントロールのコピーが、ガベージコレクタで正常に掃除されるのか不安です)
余談に少し書きましたが、メインフォームにコードの記述が集中しており、他クラスに機能を分けたいと考えています。
その場合は、メインフォームのみ分けたクラスとのやり取りを許可して、クラス分割をイメージしています。

ただ、Formで扱われるコントロールは、そのForm内で掃除(ガベージコレクタ)してあげないといけない投稿を見た覚えがあります。
他クラスからの操作はしない方がいいとも考えています。
恐らく、この話はControlsに組み込んだコントロールのことを指しているんだとは思うんですが。
this.Controls.Add(this.コントロール名);

MSDNの記事が見つけられないため、どちらにしようか悩んでおります。
> そう考えると、配列でFormコントロールを他クラスのメソッドに渡して使うのは、場合によって、いいんでしょうか?

上記コードの場合、コントロールの参照を配列で渡しているだけですので
Form 側で解放処理が行われるから問題ありません。


> 余談に少し書きましたが、メインフォームにコードの記述が集中しており、
> 他クラスに機能を分けたいと考えています。

Mediator パターンとかうまく使うと少しは幸せに慣れるかも知れません。

http://www.techscore.com/tech/DesignPattern/Mediator.html


実際いまの仕事でも地図コントロールを中心に
Botton・TextBox・ComboBox・Date・Number・DataGridやらトータル150個以上!!
と大量のコントロールを持ち、コントロールの状態が状況に応じて変化する上
料金計算やら経路検索等、複雑な処理を行う凄まじい画面があるのですが、

機能ごとにユーザーコントロールに分割して管理し、
コントロール間同士の連携は Mediator パターンで対応するようリファクタリングを行い

状況によって変化するボタンイベントは
Command と AbstractFactory パターンを使って切り分けたので
画面のコード自体は 1000行程度のコードに綺麗に収まってます。
仕様変更もかなり頻繁に出てるのですが、全く苦になりません。

パターンを実戦で使えるようになると、かなり幸せになりますよ。

http://www.hyuki.com/dp/cat_index.html
>> そう考えると、配列でFormコントロールを他クラスのメソッドに渡して使うのは、場合によって、いいんでしょうか?
>上記コードの場合、コントロールの参照を配列で渡しているだけですので
>Form 側で解放処理が行われるから問題ありません。
例えれば、社内秘の住所録を手帳に書いて持ち出したようなものですかねー。
コントロール(実際の家屋)をコピーしたわけではないので、コントロールの後始末とは
関係ないという考えで合ってると思います。

デザイン画面で追加したコントロールについては、自動で InitializeComponent に
初期化処理が書かれると思います。これがコンストラクタで呼ばれます。破棄するときは
Dispose です。InitializeComponent で作成されたオブジェクトを破棄するようなコードが
自動で書かれています。
ここまでは Form 内にコーディングされていますので、
> ただ、Formで扱われるコントロールは、そのForm内で掃除(ガベージコレクタ)してあげないといけない投稿を見た覚えがあります。
を満たしていると思います。自分でコードから追加したコントロールについては
自分で後始末も考える必要があるかと思います。
# ちなみにガベージコレクタは呼び出しません。ガベージコレクタの対象にするだけです。

問題が起こるとすれば、コントロール(実際の家屋)が破棄されてるのにアドレス参照(手帳の住所)が
残っていた場合に壊れた家を訪れることになること。
また、本来、社内秘のコントロール(家屋)に外部から破棄命令を出せること。
以上のことから、
> 他クラスからの操作はしない方がいいとも考えています。
という考え方が出てきます。

このあたりはオブジェクト指向とか、カプセル化あたりを取り扱った書籍で解説
されていそうですが、拾い読みして本来の意味が把握できていない印象を受けました。
所詮、概念なので、それに引きずられる必要はありませんが、そういった考え方を実現
できるような設計も可能です。

今回の場合、考え方としては全く逆で、出来上がったコントロールを配列に入れて
共通処理を行なうのではなく、複数の TextBox を管理するような部品を作っておいて
それを Form1、Form2 で使うとか。
http://www.atmarkit.co.jp/fdotnet/dotnettips/324winbaseform/winbaseform.html

いや「デザイン画面では自由に設計したコントロールに対して、共通機能だけ付ける
必要がある」となったら、外部モジュールに渡すのではなく、内部クラスに渡して
処理する形にするとか。
> 今回の場合、考え方としては全く逆で、出来上がったコントロールを配列に入れて
> 共通処理を行なうのではなく、複数の TextBox を管理するような部品を作っておいて
> それを Form1、Form2 で使うとか。

この考え方は大いに賛成です。

> http://www.atmarkit.co.jp/fdotnet/dotnettips/324winbaseform/winbaseform.html

ただし上記サイトのように基本フォームのデザイナで共通化を図るのは、
仕様が固まり切ってるならいいのですが、そうでなければあまりお勧めできません。

実際いまの現場でも、基本フォームに部品配置しているプロジェクトは
仕様変更に対して融通が効かず、デザインの修正に苦しんでます。

> 複数の TextBox を管理するような部品

を作るならユーザーコントロールにまとめて管理した方が
はるかに使いやすい、というのが私の実感です。

http://msdn.microsoft.com/ja-jp/events/dd229370.aspx
ひらぽんさん、るしぇさん、ご回答ありがとうございました。
 お二方の説明を読んでもやもやが解消しました。

>上記コードの場合、コントロールの参照を配列で渡しているだけですので
>Form 側で解放処理が行われるから問題ありません。
>コントロール(実際の家屋)をコピーしたわけではないので、コントロールの後始末とは
>関係ないという考えで合ってると思います。
 お二方のご意見を見て納得いたしました。
 参照しただけか、それともコピーしたものを渡したのか理解していなかったので助かりました。

>例えれば、社内秘の住所録を手帳に書いて持ち出したようなものですかねー。
>問題が起こるとすれば、コントロール(実際の家屋)が破棄されてるのにアドレス参照(手帳の住所)が
>残っていた場合に壊れた家を訪れることになること。
>また、本来、社内秘のコントロール(家屋)に外部から破棄命令を出せること。
 なるほど、セキュリティの観点から外部へ操作はしない方がいいんですね。
 外部から操作できてしまうと、誤った操作で終了させてしまうなんてこともありえますね。
 私のあやふやな疑問に答えて頂いたおかげで、もやもやが解消されました。

>このあたりはオブジェクト指向とか、カプセル化あたりを取り扱った書籍で解説
>されていそうですが、拾い読みして本来の意味が把握できていない印象を受けました。
>所詮、概念なので、それに引きずられる必要はありませんが、そういった考え方を実現
>できるような設計も可能です。
 お恥ずかしい話ご指摘通りで、
MSDNを読んでいても理解しきれずに消化不良を起こすことが多々あります。
 こうやって丁寧に説明されてると読みやすくて理解しやすいです。
 ありがとうございました。

>実際いまの仕事でも地図コントロールを中心に
>Botton・TextBox・ComboBox・Date・Number・DataGridやらトータル150個以上!!
>画面のコード自体は 1000行程度のコードに綺麗に収まってます。
>仕様変更もかなり頻繁に出てるのですが、全く苦になりません。
 コードの書き方によっては1000行程度に収まるんですね。
 せいぜい、インターフェースのポリモと基本的な継承ぐらいしか使っていない私にとって、
デザパタはハードル高いですが、少しずつ利用できるように精進します。
 デザパタは複数のケースを応用して使うものなんですね。道は険しそうです。

>いや「デザイン画面では自由に設計したコントロールに対して、共通機能だけ付ける
>必要がある」となったら、外部モジュールに渡すのではなく、内部クラスに渡して
>処理する形にするとか。
 内部クラスのことを忘れていました。
 少し考えてみようと思います。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板