Moduleをクラスに移行するには


ぴろ  2009-04-17 21:52:16  No: 141838

はじめまして  VB6からVB2005へ移行途中の者です。クラスの使い方で躓いております。
下記3点について御教授お願いします。

①変数のスコープ&クラスの寿命

  下記のようなデジタル出力ボードを制御するクラスを作成しました。
  Moduleに関数をまとめたときには問題なかったのですが、クラスのすると
  初期化(Init_Dio)のときに取得したhDeviceHandleが実際に使うSet_Dioでは
  ゼロになってしまいます。同様にポートの出力状態を保持しているOutBufも
  ゼロになってしまいます。クラスではPublicをつけて宣言しても値が保持されない
  のでしょうか?
  
Public Class FormMain
    Private Sub FormMain_Load(省略) Handles MyBase.Load
        Dim Dio3232 As Dio = New Dio
        Dio3232.Init_DIO()              ←  Dioを初期化します
    End Sub
End Class

Public Class Dio
    Public Shared OutBuf(3) As Integer        ←Sharedをつけると値は保持される
    Public Shared hDeviceHandle As Long       

    'Public hDeviceHandle As Long              ←Sharedがないと値が保持されない

     'ここで  DIOを初期化します
    Public Sub Init_DIO()
        hDeviceHandle = IFCDIO_ANY.DioOpen(lpszName, IFCDIO_ANY.FBIDIO_FLAGE)
    End Sub
    
    '実際に使うときにハンドルがゼロになってしまいます
    Public Sub SetDio(ByVal Data As Integer)
        OutBuf(PortNo) = OutBuf(PortNo) Or Bit
        st = IFCDIO_ANY.DioOutputByte(hDeviceHandle, PortNo, mOutBuf(PortNo))
    End Sub
End Class

②クラス内でのConst宣言

   Public Const RANGE1 As Integer = 1  の様にクラス内でPublicをつけてConst宣言を   しましたが、他のクラスやフォームで参照できませんでした。同じ内容をModuleに
    書くとOKでした。プロジェクト内のすべてのクラス、フォームから参照できる
    定数を宣言するにはModuleに書くしかないのでしょうか?

③クラスからフォームに貼り付けたコントロールを使う

  下記は、シリアルポートクラスのポートオープンの部分なのですがフォームロード
  するときにPortOpenメソドを実行しています。PortOpenメソドでは、各種設定をして
  フォームに貼り付けたコントロールを実行してポートをオープンします。
  クラスのメソドから直接フォーム上のコントロールを使うのは良くない・・・と
  聞きましたがこのような場合は、クラスの考え方としてどの様にするのが正しい
  のでしょうか?
    
  測定器を動かす通信部分もTesterクラスに入れてしまった方が良い気もしますし、
  汎用性を考えるとフォームに貼り付けたコントロールはクラスから操作しない方が
  が良い気もします。現在試行錯誤の状態です

Public Class Tester
    Public Sub PortOpen()
        'COM1 RS232Cの設定
            With FormMain.ComRS232
                .PortName = "COM1"
          内容省略
            End With
            FormMain.ComRS232.Open()            '  通信ポートを開く
    End Sub
End  Class
    
    よろしくお願いします


Hongliang  2009-04-17 22:54:57  No: 141839

hDeviceHandle は、Dio のインスタンスごとに存在します。
> Private Sub FormMain_Load(省略) Handles MyBase.Load
>     Dim Dio3232 As Dio = New Dio
>     Dio3232.Init_DIO()              ←  Dioを初期化します
> End Sub
で New した Dio のインスタンスじゃただのローカル変数で、このメソッドを抜ければ使われなくなります。
SetDio を使うときにまた別に Dio を New しているでしょう?  そうすると別の Dio ですから別の hDeviceHandle を持つことになります。
メソッドを超えてインスタンスを維持する場合はフィールド(メンバ変数)に置いておかなければなりません。
あ、それから VB6 以前と VB7(VB2002)以降では Integer/Long のサイズが変わっています。あとハンドル/ポインタ用に IntPtr 型が存在しています。

> Public Const RANGE1 As Integer = 1  の様にクラス内でPublicをつけてConst宣言を
> しましたが、他のクラスやフォームで参照できませんでした。同じ内容をModuleに
> 書くとOKでした。プロジェクト内のすべてのクラス、フォームから参照できる
> 定数を宣言するにはModuleに書くしかないのでしょうか?
Module の場合はコンパイラが適当に変数名から探してきますが、クラス等に定義した静的メンバ(Const/Shared なもの)を参照するには  クラス名.変数名  と記述する必要があります。
どのクラスに存在するものなのかぱっと見で分かる、そのため「どういう機能に属するどういう値なのか」を直感的に記述・読解できる、同名の静的メンバを(別クラスに配置することで)使い分けることができる、などのメリットがあります。
なお、Const 定数は基本的に絶対不変のものに使い(Math.PI とか)、そうでないものは Shared ReadOnly な変数にしておくのがいいでしょう。

> クラスのメソドから直接フォーム上のコントロールを使うのは良くない・・・と
> 聞きましたがこのような場合は、クラスの考え方としてどの様にするのが正しい
> のでしょうか?
メソッドの引数として渡せばいいんじゃないですか?


オショウ  2009-04-18 01:12:52  No: 141840

①  変数のスコープ&クラスの寿命

IFCDIO?ですか、インターフェース社のライブラリ使っておられる
ようにお見受けしますが・・・違いますか?

仮にインターフェース社のものとして、私も過去、モジュールで
はなく、クラスに・・・と何度かメーカーに申し入れしたのです
が残念ながら聞いてもらえなかったので自作しました。

で・・・
> Public Class FormMain
>     Private Sub FormMain_Load(省略) Handles MyBase.Load
>         Dim Dio3232 As Dio = New Dio
>         Dio3232.Init_DIO()              ←  Dioを初期化します
>     End Sub
> End Class

    これだと、Hongliangさんが書かれたようにFormMain_Loadを
    抜けたらDio3232インスタンスは破棄されますので・・・

Public Class FormMain

    Private Dio3232 as Dio

    Private Sub FormMain_Load(省略) Handles MyBase.Load
        Dio3232 = New Dio
        Dio3232.Init_DIO()
    End Sub

    Private Sub FormMain_FormClosing ...

      Dio3232 = Nothing

    End Sub

End Class

    とするところなんですが、DioクラスをiDisposable属性を
    つけていないと、アクセスしていない時にGCで破棄されて
    しまう可能性がありますので、
    Implaments iDisposable をDioクラスに付けたら・・・
    よいと思います。これで私はうまくいってます。

※  スペル間違っていたらすいません。

②  クラス内でのConst宣言

    Hongliangが書かれている通りです。
    固定値で変化しないなら・・・です。

③  クラスからフォームに貼り付けたコントロールを使う

    フォームにコントロールを貼る場合も、自作シリアル通信
    クラスを使うにせよ、どちらでもOKかと。
    私は汎用性を求めて自作クラスにしてます。
    画面の無いWindowsサービス中からでも通信が可能なように
    と、いろいろな機器との通信に利用できるように、派生させ
    て機能をオーバーライドし、機器専用通信機能として実現さ
    せてます。パフォーマンス的にもまず問題はありません。

がんばって下さい!

※  参考まで
    インターフェースのDIOカードのドライバーは共有オープン
    すると、複数のインスタンスからのアクセスも可能ですが
    ビットをオンするのとオフするのが競合した場合、エラー
    が出ません。必ず最後に実行された方が結果となります。
    また、マルチスレッドや複数のタイマー内で同一インスタ
    ンスのDIOをアクセスした場合も同様です・・・
    よって、排他制御を入れてやる必要があります。
    System.Threading.ReaderWriterLock を使って排他制御し
    てます。効果はほとんど解らないと思いますが・・・

以上。


ぴろ  2009-04-18 06:23:16  No: 141841

Hongliang様  オショウ様  ありがとうございます。
本を読んで  クラスはクッキーの型  なんて説明でクラスを理解したつもりでも
実践ではなかなか上手くいかないですね。

①  変数のスコープ&クラスの寿命
お察しの通りインターフェース社のライブラリ(IFCDIO)を使用しています。

☆メンバ変数について 自分では

Public Class Dio
   Private hDeviceHandle As IntPtr  ←メンバ変数のつもり

    Public Sub Init_DIO()
        hDeviceHandle = IFCDIO_ANY.DioOpen(lpszName, IFCDIO_ANY.FBIDIO_FLAGE)
        ↑メンバ変数に代入しているつもり
    End Sub

だったのですが・・・・。

☆Implements IDisposable
>  Private Sub FormMain_FormClosing ...
>      Dio3232 = Nothing
>    End Sub
Implements IDisposableについて調べてみました。
これを追加するとガベージコレクタで回収されなくなるようですが・・・。
また、Public Sub Dispose() Implements IDisposable.Disposeが自動的に
追加されましたが  Dio3232 = Nothingも  必要なのでしょうか?

☆OutBuf変数も値を保持したいのですが・・・
この変数はデジタル出力ボードの出力ビットの情報を保持するために
使っています。
Public Class Dio
    Public  OutBuf(3) As Integer        ←メンバ変数のつもり
       
    Public Sub SetDio(ByVal Data As Integer)
        Dim PortNo As Integer
        Dim Bit As Integer

        PortNo = Data \ &H100
        Bit = Data Mod &H100
        OutBuf(PortNo) = OutBuf(PortNo) Or Bit
        st = IFCDIO_ANY.DioOutputByte(hDeviceHandle, PortNo, moutbuf(PortNo))
        If st <> 0 Then
            MessageBox.Show("Output Data failed", "PCI2726Cエラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End If
    End Sub

呼び出し元は、
   Private Sub BackgroundWorker1_RunWorkerCompleted(省略) Handles BackgroundWorker1.RunWorkerCompleted
        Dim Dio3232 As Dio = New Dio
        'テスト結果出力
        Dio3232.SetDio(TEST_RSLT1)
   End Sub
このコードでは、OutBufの中身がゼロになってしまいます。
たぶんクラスの考え方が間違っているのだと思いますが・・・・。何が原因なのでしょうか?

②  クラス内でのConst宣言

>そうでないものは Shared ReadOnly な変数にしておくのがいいでしょう。

Public Class Dio
    Implements IDisposable

    Public Shared ReadOnly  Sw1 as Integer = &H1
    Public Shared ReadOnly  Sw2 as Integer = &H2

こんな感じでしょうか?

③クラスからフォームに貼り付けたコントロールを使う

>メソッドの引数として渡せばいいんじゃないですか?

「VB.Net  メソドの引数」で検索したのですが、答えが見つかりませんでした。
コントロールをメソドの引数として渡す  とはどのような事なのでしょうか?

お手数をおかけして申し訳ありません
よろしくお願いします。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加