Excel 2007でVBAプログラミングを行っており、すべてのデータシートを別のシートにコピーする1つのワークブックがあります。新しいシートにはいくつかのヘッダー行があり、それらがどこにあるのかを追跡したいので、常に単語を見つける必要はありません。
Excelドキュメントが開いている間、クラスを使用してそれらを実行し続けるのが最も簡単ですか?それとも、これが重くて扱いにくくなり、サブルーチンを使い続ける必要がありますか?クラスを使用する利点は何ですか?いくつかのオブジェクトがあるのではなく、列とシートの検証だけです。
サブルーチンだけでなくクラスを使用する利点は、クラスがよりクリーンなコードを記述できるレベルの抽象化を作成することです。確かに、VBAでクラスを使用したことがない場合、学習曲線がありますが、それを理解するのに時間をかける価値があると確信しています。
クラスに切り替えるべき重要な指標の1つは、関数とサブルーチンにパラメーターを絶えず追加している場合です。この場合、ほとんどの場合、クラスを使用するのが最善です。
クラスの説明を 以前のStack Overflowの回答の1つ からコピーしました。
クラスを使用するとどのように役立つかを示す長い例を次に示します。この例は長くなりますが、オブジェクト指向プログラミングのいくつかの原則がコードのクリーンアップに実際にどのように役立つかを示します。
VBAエディターで、_Insert > Class Module
_に移動します。 [プロパティ]ウィンドウ(デフォルトでは画面の左下)で、モジュールの名前をWorkLogItem
に変更します。次のコードをクラスに追加します。
_Option Explicit
Private pTaskID As Long
Private pPersonName As String
Private pHoursWorked As Double
Public Property Get TaskID() As Long
TaskID = pTaskID
End Property
Public Property Let TaskID(lTaskID As Long)
pTaskID = lTaskID
End Property
Public Property Get PersonName() As String
PersonName = pPersonName
End Property
Public Property Let PersonName(lPersonName As String)
pPersonName = lPersonName
End Property
Public Property Get HoursWorked() As Double
HoursWorked = pHoursWorked
End Property
Public Property Let HoursWorked(lHoursWorked As Double)
pHoursWorked = lHoursWorked
End Property
_
上記のコードは、使用しているデータに固有の厳密に型指定されたオブジェクトを提供します。多次元配列を使用してデータを保存する場合、コードは次のようになります。arr(1,1)
はID、arr(1,2)
はPersonName、arr(1,3)
はHoursWorkedです。その構文を使用すると、何が何であるかを知るのは困難です。オブジェクトをまだ配列にロードしていると仮定しますが、代わりに上記で作成したWorkLogItem
を使用します。この名前は、arr(1).PersonName
を実行して人の名前を取得できます。これにより、コードがはるかに読みやすくなります。
この例で動き続けましょう。オブジェクトを配列に保存する代わりに、collection
を使用してみます。
次に、新しいクラスモジュールを追加し、ProcessWorkLog
と呼びます。そこに次のコードを入れます:
_Option Explicit
Private pWorkLogItems As Collection
Public Property Get WorkLogItems() As Collection
Set WorkLogItems = pWorkLogItems
End Property
Public Property Set WorkLogItems(lWorkLogItem As Collection)
Set pWorkLogItems = lWorkLogItem
End Property
Function GetHoursWorked(strPersonName As String) As Double
On Error GoTo Handle_Errors
Dim wli As WorkLogItem
Dim doubleTotal As Double
doubleTotal = 0
For Each wli In WorkLogItems
If strPersonName = wli.PersonName Then
doubleTotal = doubleTotal + wli.HoursWorked
End If
Next wli
Exit_Here:
GetHoursWorked = doubleTotal
Exit Function
Handle_Errors:
'You will probably want to catch the error that will '
'occur if WorkLogItems has not been set '
Resume Exit_Here
End Function
_
上記のクラスは、WorkLogItem
のコレクションで「何かをする」ために使用されます。最初に、作業時間の合計数をカウントするように設定しました。書いたコードをテストしましょう。新しいモジュールを作成します(今回はクラスモジュールではなく、「通常の」モジュールのみ)。モジュールに次のコードを貼り付けます。
_Option Explicit
Function PopulateArray() As Collection
Dim clnWlis As Collection
Dim wli As WorkLogItem
'Put some data in the collection'
Set clnWlis = New Collection
Set wli = New WorkLogItem
wli.TaskID = 1
wli.PersonName = "Fred"
wli.HoursWorked = 4.5
clnWlis.Add wli
Set wli = New WorkLogItem
wli.TaskID = 2
wli.PersonName = "Sally"
wli.HoursWorked = 3
clnWlis.Add wli
Set wli = New WorkLogItem
wli.TaskID = 3
wli.PersonName = "Fred"
wli.HoursWorked = 2.5
clnWlis.Add wli
Set PopulateArray = clnWlis
End Function
Sub TestGetHoursWorked()
Dim pwl As ProcessWorkLog
Dim arrWli() As WorkLogItem
Set pwl = New ProcessWorkLog
Set pwl.WorkLogItems = PopulateArray()
Debug.Print pwl.GetHoursWorked("Fred")
End Sub
_
上記のコードでは、PopulateArray()
は単にWorkLogItem
のコレクションを作成します。実際のコードでは、Excelシートまたはデータオブジェクトを解析してコレクションまたは配列を埋めるためのクラスを作成できます。
TestGetHoursWorked()
コードは、クラスがどのように使用されたかを単に示しています。 ProcessWorkLog
がオブジェクトとしてインスタンス化されていることがわかります。インスタンス化された後、WorkLogItem
のコレクションはpwl
オブジェクトの一部になります。これはSet pwl.WorkLogItems = PopulateArray()
行で確認できます。次に、コレクションWorkLogItems
に作用する関数を呼び出します。
なぜこれが役立つのですか?
データが変更され、新しいメソッドを追加するとします。 WorkLogItem
にHoursOnBreak
のフィールドが含まれており、それを計算する新しいメソッドを追加するとします。
必要なことは、次のようにWorkLogItem
にプロパティを追加することだけです:
_Private pHoursOnBreak As Double
Public Property Get HoursOnBreak() As Double
HoursOnBreak = pHoursOnBreak
End Property
Public Property Let HoursOnBreak(lHoursOnBreak As Double)
pHoursOnBreak = lHoursOnBreak
End Property
_
もちろん、コレクションを作成するためのメソッドを変更する必要があります(使用したサンプルメソッドはPopulateArray()
でしたが、このために別のクラスを用意する必要があります)。次に、新しいメソッドをProcessWorkLog
クラスに追加します。
_Function GetHoursOnBreak(strPersonName As String) As Double
'Code to get hours on break
End Function
_
TestGetHoursWorked()
メソッドを更新してGetHoursOnBreak
の結果を返す場合、次の行を追加するだけで済みます。
_ Debug.Print pwl.GetHoursOnBreak("Fred")
_
データを表す値の配列を渡した場合は、配列を使用したコード内のすべての場所を見つけて、それに応じて更新する必要があります。代わりにクラス(およびそれらのインスタンス化されたオブジェクト)を使用すると、コードを簡単に更新して変更を処理できます。また、クラスを複数の方法で使用できるようにすると(おそらく、1つの関数に必要なオブジェクトプロパティは4つだけで、別の関数には6つ必要になります)、同じオブジェクトを参照できます。これにより、さまざまなタイプの関数に複数の配列を使用できなくなります。
さらに読むには、高くVBA Developer's Handbook、2nd edition のコピーを取得することをお勧めします。この本には、優れた例とベストプラクティス、大量のサンプルコードが満載です。深刻なプロジェクトのためにVBAに多くの時間を費やしているなら、この本を調べるのに十分な時間があるでしょう。
多数のサブルーチンがある場合、またはサブルーチンが非常に長い場合は、クラスにコードを構造化すると役立つ場合があります。たとえば、それぞれがわずか10行のコードであるサブルーチンが2つしかない場合は、これは過剰終了です。コードをクラスに構造化することの利点は、後から戻ってきたときに読みやすく、変更しやすいことです。そのため、コードをクラスに構造化するもう1つの理由は、コードを変更する必要がある可能性が高い場合です。
他の貢献者が述べた利点に追加できるものがもう1つあります(Ben McCormackの優れた答えのどこかにあり、それを見逃してしまった場合は申し訳ありません)。 VBAスクリプトが何らかの時点で再プログラムされる可能性が高い場合、クラスを使用できます。
例えば、私は一種の注文管理システムを設計しています。これはかなりの数の同僚によって使用されることになっていますが、順序付け規則が変更された場合は再プログラミングが必要になる場合があります。そのため、ストックアイテムに関するすべての情報を収集する基本的なストックアイテムクラスを設計しました。ただし、任意の順序でこのデータを分析する方法に関するルールは、簡単にアクセスでき、コメントの多いサブルーチンで記述されています。これを行うことで、将来のVBAプログラマーが、特定のストックアイテムに関するすべてのデータの収集方法に対処することなく、注文が生成される数学ルールを簡単に変更できることを願っています(これはすべて、クラス内のサブルーチンと関数によって行われます、クラスに在庫番号が渡されるとアクティブになります)。クラスのパブリックプロパティはインテリセンスによっても選択されるため、次のプログラマも自分自身も簡単に過ごすことができます。
ポイントは、クラスがこの方法で後のユーザーの生活を楽にすることができるということだと思いますif基本的な情報のセット、または常に概念のオブジェクトをエンコードします。プログラムの使用。