掲示板システム
ホーム
アクセス解析
カテゴリ
ログアウト
インスタンス同士でお互いに呼び出す (ID:102165)
名前
ホームページ(ブログ、Twitterなど)のURL (省略可)
本文
とりあえず提示されたコードに手を加えるとしたら、まずクラスAの中に何かパブリック変数を用意して下さい。 クラス内のPublic変数は自動的に公開プロパティとして機能しますので、例えばクラスAの先頭に Public TestData as Long と記述しておけば、A(i).TestDataでその変数にアクセスが出来るようになるでしょう。 クラスの外から値を入れたり出したりする事はこれだけで実現できるようになります。 やりたい事はこれだけでも実現できるかもしれませんね。 でも、クラスを作るとき通常こういったPublicな変数宣言はあまりやらないと思います。 これだけではユーザー定義型と同じ機能でしかないですよね。 であれば、そもそもユーザー定義型で事足りますから、 わざわざクラスとか言う「よくわからないもの」を使う必然性もないわけです。 繰り返しになりますが、クラスがなぜ必要なんですか? ただのデータの入れ物が欲しいなら、クラスではなく 扱いも容易なただの変数やユーザー定義型を使うようアドバイスするところです。 おそらくクラスについての理解はまだ乏しく、 その勉強がてら何かを作ってらっしゃるんだろうと勝手に解釈しています。 というわけで、以下クラスの存在意義について実例コード付きの基本的な説明です。 ウルトラ長文です、うんざりするのを覚悟して下さいねーw 外から見たときのインスタンスの扱い方、A.Dataとか・・・どこかで見たことはありませんか? これってユーザー定義型にそっくりじゃない? クラスとは基本的に「処理機能を持ったデータ格納庫」です。 ユーザー定義型はまさにこの「データ格納庫」と言えます。 その点においてはユーザー定義型の宣言とクラスは存在意義がよく似ている部分があります。 ではその違いである「処理機能」とは何か? 通常クラスは自身の内部に変数を持ちます。 しかしこの変数は直接外部に公開はせず、クラス自身が利用するために宣言します。 クラスの外部からは、専用のプロパティを通じて値を出し入れ出来るようコーディングするのです。 クラスとは「ユーザー定義型に似た外観を持たせた、関数のかたまり」とも言えます。 以下クラスの機能についてユーザー定義型を用いた例で説明してみます。 例:「文字数が○○個以下の文字列を格納するデータ型を作る」 この命題を専用のユーザー定義型と専用関数で実現するものが以下のコードです。 フォームにテキストボックスを2つとボタンを1つ作り、このフォーム内に以下のコードを記述します。 ──────────────────────────────────────── Option Explicit Private Type LimitedString Text As String '制限文字数以下の文字を格納 Limit As Long '制限文字数を格納 End Type Dim Words As LimitedString Private Sub SetText(X As LimitedString, InputWords As String) X.Text = Left(InputWords, X.Limit) '制限以上の文字を切り捨てた結果を代入 End Sub Private Sub Command1_Click() SetText Words, Text1.Text Text2.Text = Words.Text End Sub Private Sub Form_Load() Words.Limit = 10 End Sub ──────────────────────────────────────── ボタンを押せばText1を入力として文字数制限した結果がText2に表示されるというものです。 以下動作説明。 >Private Sub Form_Load() > Words.Limit = 10 >End Sub フォーム起動時に、制限する文字数を10文字に設定しています。 >Private Sub Command1_Click() > SetText Words, Text1.Text > Text2.Text = Words.Text >End Sub テキストボックスに文字を入力してボタンをクリックすると、 その値を専用関数を介してWordsに代入しています。(専用関数SetTextについては後述) 代入処理が終わるとWords.Textの内容をText2に表示します。 まず先頭の宣言で専用のユーザー定義型変数を作っています。 >Private Type LimitedString > Text As String '制限文字数以下の文字を格納 > Limit As Long '制限文字数を格納 >End Type > >Dim Words as LimitedString この変数Words.Textには文字数の制限ルールを設けたいものとします。 制限する文字数はWords.Limitの数で決まるものとします。 Words.Limitが100であれば、Words.Textには100文字までしか代入できません。 100文字以上代入しようとした場合は100文字に切り詰められるものとします。 ・・・と、決めたルールですが、 ユーザー定義型には入力規制なんていう都合のいいものは備わっていません。 ルールに従うためには、このLimitedString型には直接アクセスしてはならないものとし、 代入をする際には専用の関数を通して行うようにします。 >Sub SetText (X as LimitedWords, InputWords as String) > X.Text = Left(InputWords, X.Limit) '制限以上の文字を切り捨てた結果を代入 >end Sub それがこのSetText関数ですが、超過分を切り捨てて.Textへの入力を代行しています。 文字数を制限するためのフィルタのような役割をしているわけです。 一応以上でルール通りの管理が行えるようになり、ユーザー定義型と専用関数を用いて目的を達成しました。 しかし、元々このWordsは所詮ただのユーザー定義型変数です。 この変数にはいつでも直接アクセスが可能で、専用関数を介さず誤った代入をする可能性は否定し切れません。 またLimitは文字数であり、常に0以上の正数である必要がありますが、現状そのチェックはありません。 実際にはLimitの代入の際も専用関数で入力を制限する必要があることがわかります。 おまけに文字列を代入した後にLimitを変更した場合、 すでに入力された文字列と新たなLimit数の間には関連性が無くなってしまいます。 すると専用関数の数もさらに増えていく事になります。 このように、特定の変数とそれを管理する専用関数は常にセットです。 その変数は専用関数がなければ操作できませんし、逆に専用関数はその変数のためだけに存在します。 利用する側から見れば、単にユーザー定義型変数への入出力をしたいだけなのですが、 専用の関数が多くなればなるほど、その取り扱いは混乱を極めていくことになります。 この値に代入するときの関数名って何だっけ?とか、この値は直接代入してもいいんだっけ?とか。 このような場合、まさにクラスの出番と言えます。 クラスはデータと「処理機能」を同時に持つ事が出来ます。 上記のユーザー定義型変数と専用関数の両方を内部に持てるという事です。 以上と同じ実装をクラスで考えてみます。 まずclsLimitedStringクラスを作成し、以下のように記述します。 ──────────────────────────────────────── Option Explicit Private LimitedText As String '100文字以下の文字だけを格納 Private LimitCount As Long '制限する文字数を格納 Public Property Let Text(InputWords As String) LimitedText = Left(InputWords, LimitCount) 'Limit数以降を切り捨てた結果を代入 End Property Public Property Get Text() As String Text = LimitedText '変数の値をそのまま返すだけ End Property Public Property Let Limit(Count As Long) If Count >= 0 Then '制限文字数が0以上でなければ代入させない LimitCount = Count '制限文字数を代入 Me.Text = LimitedText '現在格納している文字をもう一度代入し再チェックする End If End Property Public Property Get Limit() As Long Count = LimitCount '変数の値をそのまま返すだけ End Property ──────────────────────────────────────── フォームにテキストボックスを2つとボタンを1つ作り、このフォーム内に以下のコードを記述します。 ──────────────────────────────────────── Option Explicit Dim Words As clsLimitedString Private Sub Command1_Click() Words.Text = Text1.Text Text2.Text = Words.Text End Sub Private Sub Form_Load() Set Words = New clsLimitedString Words.Limit = 10 End Sub ──────────────────────────────────────── 以上。 これを実行するとユーザー定義型のサンプルと同じ動作をします。 見てわかる通り、Form側のコード量が少なくなっています。 それにForm側から見るとほぼユーザー定義型と同じ使い勝手で利用できるのがわかります。 以下コードの説明。 >Private LimitedText As String '100文字以下の文字だけを格納 >Private LimitCount As Long '制限する文字数を格納 先頭で内部変数を宣言しています。前述のサンプルでのユーザー定義型に相当するものです。 いずれもPrivateで宣言しているのは外部からアクセス不能にするためです。 (この宣言しかないクラスは外部から見ると何も見えない謎のクラスになるでしょうね) 次に、外部からデータにアクセス出来るようにプロパティを作成します。 同時に前サンプルの専用関数SetTextが行っていた制限処理をここに組み込みます。 >Public Property Let Text(InputWords As String) > LimitedText = Left(InputWords, LimitCount) 'Limit数以降を切り捨てた結果を代入 >End Property 普段「Sub」か「Function」の2種類を使って関数という名のコードの集まり(ステートメント) を書いていると思いますが、プロパティでは「Property」という単語で宣言します。 SubとFunctionの書き方が良く似ているように、Propertyもそっくりで簡単です。 この時点で、このクラスはプロパティとしてTextという名前が見えるようになります。 しかし代入は出来ますが、参照は出来ない状態です。 Words.Text = "hoge" は可能ですが、 Text2.Text = Words.Text のほうはエラーで実行できないという事です。まさに片手落ちですね。 プロパティの宣言で書いた「Property Let」は代入の処理を記述するためのものです。 参照の処理を記述するにはこれとは別にもうひとつ「Property Get」を記述する必要があります。 >Public Property Get Text as String > Text = Words.Text '変数の値をそのまま返すだけ >end Sub ここで、出力用のプロパティを宣言します。 関数で言えば戻り値にWords.Textを渡しているのと等しい処理内容になっています。 さらにLimitCountについても入出力するためにLimitプロパティを作成しています。 >Public Property Let Limit(Count As Long) >Public Property Get Limit() As Long ここには、前サンプルには無かった制限文字数のチェック > If Count >= 0 Then '制限文字数が0以上でなければ代入させない > LimitCount = Count '制限文字数を代入 > End If と、既存文字列に対して新制限文字数の適用をリアルタイムに行う処理 > Me.Text = LimitedText '現在格納している文字をもう一度代入し再チェックする を加えてあります。 このように、クラスはユーザー定義型の能力と+関数の能力という2つの能力を持ったものです。 使うときは変数と同じように振る舞い、しかしその中身はいずれも関数みたいなものなんですね。 「変数であり関数である」という2つの側面を持つとも言えます。 「データの集まり」+「処理機能」ですよと言った意味、これでわかったかなぁ。 クラスを使用すると、それを利用する場面でとてもコーディングが楽になるわけです。 可読性も向上しますし、書いてて混乱する事もも少なくなります。 しかし逆に、ユーザー定義型の宣言のように数行で簡単に済ますことが出来なくなりますし、 NewしたりNothingを入れたりと管理の手間も多少増えます。 そもそもユーザー定義型に比べ処理コストが大幅に多くなります。(処理時間や消費メモリが増加します) なによりクラス自体の設計は結構大変です。 クラスのコーディング途中はわりと混乱しがちです。(私のクラス設計が悪いのかもしれませんが) で、話は戻ってクラスを使う目的はなんですか? 使おうと思えば、クラスは取るに足らない用途にも使えます。 使えますがコーディングが大変になるだけですよ。 VB6では1クラス1モジュールなので管理モジュール数も無駄に増えます。 クラスをクラスとしてちゃんと活かせないなら、使っても良い事なんて本当にありません。 それだけは覚えておいて下さい。 (正直、例のサンプルもしょうもない使い方の一つと言えます) 以下蛇足です。 ここまではユーザー定義型を引き合いに出して話しましたけど、もうひとつ クラスに良く似た使い方が出来る身近な存在があるんですが、わかります? それはフォームやコントロールです。 様々なプロパティがあって代入が出来たり参照が出来たりするメンバーを持っている。 フォームはクラスに非常に良く似てますよね。というかうりふたつです。 それもそのはずでフォームもクラスの一種なんですよ。(扱いがやや特殊ですけど) フォームとクラスの根本的な違いは、表示機能を持っているか持っていないかの違いだけです。 GUIを持っている関係上フォームのほうは特別扱いされてはいますが、 目に見えるというその点をのぞけば、その扱い方はクラスと同一なんです。 Propertyステートメントだってクラスモジュールでしか使えないものと思われがちですが、 実はフォームモジュールにもプロパティを記述できちゃうんですよ。 独自のプロパティやメソッドを持ったフォームだって作れるわけですね。 フォームがクラスに似た存在である事を簡単に確かめるためには、 新規コマンドボタンに以下のコードを記述して実行してみて下さい。 ──────────────────────────────────────── > Dim objForm As Form1 > Set objForm = New Form1 > objForm.Caption = Me.Caption & "のコピー" > objForm.Visible = True ──────────────────────────────────────── すると、Form1と全く同じフォームがもうひとつ出現します。 フォームのインスタンスを作ると、もうひとつフォームが出来るという例です。 ボタンを何度も押せばいくつでも、そしてどのフォームのボタンからも新しいフォームが出来ます。 これは実行中のフォームというのは、実はそれぞれがインスタンスのひとつである事を意味し、 逆説的にフォームモジュールとはクラスモジュールと同等の扱い方が出来ることを意味します。 これらの新しいフォームは元のとそっくりですが、別のインスタンスなので内部変数も別物です。 よって、前述のようなサンプルのフォームでは各テキストの内容とかもちゃんと正常に動作します。 (※いずれのサンプルも変数をフォーム内部に持たせているためそういう動作をします) New Form1という書き方はクラスの記述法とは異なりますが、 NewをSetすることで新たなインスタンスが出来上がっているのは同様ですね。 クラスと大きく異なる点は、スタートアップのフォームは自動的にNewされVisibleがTrueになる事と、 その参照はVBがこっそり保持しているという事、 フォームが消滅していてもアクセスされるとまた自動的にNewされる事くらいです。 あとこの変数にNothingを入れてもフォームは消えないし、終了しないという特殊な点にも注意してください。 このフォームを呼び出し元で消そうと思ったら >Unload objForm と記述します。普段フォームを終了させる時はUnload Meと記述していると思いますが、 このUnloadステートメントが行っているのはVBが保有しているフォームインスタンスの破棄だったわけです。 (ちなみにUnloadした後でもこのobjFormにアクセスすると上記のルールのとおりで 勝手に再度Newされてしまう危険があります。Unloadを呼んだらさっさとNothingをSetしましょう) このフォームの話はほんと完全なる蛇足ですから忘れてもらって構いません。 正直クラスの本質がわからないと何言ってるのかわかんないと思いますし。 クラスが理解できれば、この辺の今まで理解できなかった(理解する必要もなかった) 特別な存在についても実はそんなに特別ではなくクラスと同じ考えで把握出来ますよという例です。 と、ここまで書くのに4時間かかっちゃったw 以上暇ななでした。
←解決時は質問者本人がここをチェックしてください。
戻る
掲示板システム
Copyright 2020 Takeshi Okamoto All Rights Reserved.