Worksheetクラスを継承したクラスを作る(1)

Worksheetクラスを継承したクラスを作る

超絶アホ企画。

少しオブジェクト指向をかじったVBAerなら、VBAのクラスモジュールに「継承」がないことは当然知っているだろう。

だから、あくまでも「擬似継承」である。

っていうか、ほとんど嘘である。

擬似継承の方法

とにかく、

全てのメソッド・プロパティをラップする

のである。

だから、

超絶アホ企画

なんである。

PoweredSheetクラス

PoweredSheetというクラスを作り、その中にWorksheetクラスのプロパティ・メソッドを片っ端から置いていく。

とりあえずの基本形は以下の通り。

リスト1 クラスモジュールPoeredSheet
Option Explicit

'Module Level Variable'
Private realSh As Worksheet

Private isAvailable As Boolean

'Constructor'
Public Sub init(ByVal targetSheet As Worksheet)
  Set realSh = targetSheet
  isAvailable = True
End Sub

Private Sub Class_Initialize()
  isAvailable = False
End Sub

擬似コンストラクinitメソッドで、モジュールレベル変数realShWorksheetオブジェクトを突っ込む。プロパティとかメソッドは、realShを経由して取り出すことにする。

Rangeプロパティをラップする

いきなり難問。

Rangeプロパティは、通常、次のように使う。

[Worksheet].Range("A1")    '……(1)'
With ThisWorksheet
  Range(.Range("A1"), .Range("D2"))    '……(2)'
End With

(1)は、Rangeプロパティの引数に文字列(セルのアドレス)を渡している。

(2)は、Rangeプロパティの引数にRangeオブジェクト(単独のセル)を二つ渡している。

こういうわけのわからない引数を実現しなければならない。

ParamArrayを使う

そこで、ParamArrayの出番である。

ParamArrayとは、

www.tipsfound.com

によると、

可変長な引数を作成するにはParamArray 引数名() As Variantのように入力します。必ず配列の Variant 型にします。ParamArray を付けると、任意の数の引数を配列に変換して受け取れます。

とのこと。

これを使うことにした。

スト2 クラスモジュール PoweredSheet

Property Get Range部分のみ。

Public Property Get Range( _
           ParamArray args() As Variant) As Range
  Dim ret As Range
  Select Case UBound(args)
    Case 0
      Set ret = realSh.Range(args(0))
    Case 1
      Set ret = realSh.Range(args(0), args(1))
  End Select
  Set Range = ret
End Property

引数は、インデックス指定の場合一つ、セル範囲の左上端、右下端指定の場合で二つなので、Select Caseで二通りに場合分けした。不適切な引数を与えた場合は、本家Rangeプロパティでも実行時エラーになるので、エラー対応とか別に要らんやろ、という投げやり対応。

2019/10/3追記

ことりちゅん (id:Kotori-ChunChun)氏からのアドヴァイスに従って、Rangeプロパティの実装を書き換える。

みんな大好き『Office デベロッパー センター』のRangeプロパティの項によると、

Name Required/Optional Data type Description
Cell1 Required Variant The name of the range. This must be an A1-style reference in the language of the macro. It can include the range operator (a colon), the intersection operator (a space), or the union operator (a comma). It can also include dollar signs, but they're ignored.

You can use a local defined name in any part of the range. If you use a name, the name is assumed to be in the language of the macro.
Cell2 Optional Variant The cell in the upper-left and lower-right corner of the range. Can be a Range object that contains a single cell, an entire column, or entire row, or it can be a string that names a single cell in the language of the macro.

とある。

つまり、第1引数、第2引数ともにVariantにして、第2引数をOptionalにすればいいのである。

スト2改 クラスモジュール PoweredSheet
Public Property Get Range(ByVal Cell1 As Variant, _
                 Optional ByVal Cell2 As Variant) As Range
  Dim ret As Range
  If IsMissing(Cell2) Then
    Set ret = realSh.Range(Cell1)
  Else
    Set ret = realSh.Range(Cell1, Cell2)
  End If
  Set Range = ret
End Property

Optionalな第2引数はVariant型なので、IsMissingで省略されたかどうか調べることができる。今まで極力Variantを避けてきていたので、IsMissingが役に立つところを初めて見たかも知れん。

以上、追記ここまで。

使ってみる

f:id:akashi_keirin:20190916215800j:plain

このようなシートを用意して、次のコードで実験。

リスト3 標準モジュール
Private Sub testPoweredSheet()
  Dim ps As PoweredSheet
  Set ps = New PoweredSheet
  Call ps.init(Sheet1)
  Debug.Print ps.Range("A1").Value    '……(1)'
  With ThisWorkbook.Worksheets("Original")    '……(2)'
    Debug.Print ps.Range(.Range("A1"), _
                         .Range("D4")).Address
  End With
End Sub

PoweredSheetクラスのインスタンスpsSheet1を渡してから、(1)と(2)の二通りのやり方でRangeプロパティを参照。

実行結果

イミディエイト・ウインドウの表示は

f:id:akashi_keirin:20190916215803j:plain

このとおり。

おわりに

この調子で、全てのプロパティ・メソッドを実装していけば、晴れてWorksheetクラスを継承したPoweredSheetクラスが出来上がる。

がんばるぞ!(←マジで?!)

続きはコチラ

akashi-keirin.hatenablog.com

akashi-keirin.hatenablog.com

akashi-keirin.hatenablog.com

akashi-keirin.hatenablog.com

akashi-keirin.hatenablog.com

akashi-keirin.hatenablog.com