t-hom’s diary

主にVBAネタを扱っているブログです。

VBA SubとFunctionとPropertyの使い分け

VBAには3種類のプロシージャがある。SubとFunctionとPropertyだ。
しかしその守備範囲はキッチリ線引きされているわけではなく、使い分けに困る場面がある。

今回はその使い分けのヒントとなる考え方を紹介する。

なお、3種類と書いたがProperty にはLet・Set・Getがあるので実際には5種類になる。
このうちLetとSetはいずれも値を設定するという目的が同じなのでひとまとめに扱うことにする。

私がプロシージャを使い分ける際に考えていることを以下2つの表にまとめた。
f:id:t-hom:20180527212534p:plain

表が2つあるのは、何のためのプロシージャか(What)と、どのようなプロシージャか(How)の2つの観点で考える為。

What表の解説

単純な手続き

単純な手続きとは、いわゆる「マクロ」など、単純に手続きとしてのプロシージャを指す。

純粋な関数

純粋な関数とは、手続きの外で状態の変更を引き起こさず、ユーザーとの対話用ダイアログが表示されず、与えられた引数によってのみ戻り値が決まる普通の関数のこと。
たとえばLeft関数やFormat関数などが純粋関数である。

逆に「純粋でない」とは、セルの値を変更したり、参照渡しされた引数の中身を変更してしまったりといった状態の変更を引き起こすものや、現在時刻の取得など、システムの状態を取得するもの、ユーザーにメッセージを表示させたり選択肢を表示させたりといった副作用を伴う処理を含むことをいう。

状態の取得

状態とはプロシージャの外で設定されたあらゆる値を指す。
例えばVBAのNow関数はシステムから日時を取得するという意味で状態の取得にあたる。
実際、VBAのNow関数はProperty Getプロシージャで作られている。

状態の設定

状態の取得と逆にプロシージャの外に働きかけて値を変更することを指す。

How表の解説

重い処理

DBへのアクセス、Webからのデータ取得、広いセル範囲への個別アクセスなどある程度負荷が想定される処理を指す。

軽い処理

プロシージャ自体が単純で短く、連続で実行されてもそれほど負荷がかからない処理を指す。

破壊的処理

状態の変更を引き起こす処理を指す。
What表に書いた「状態の変更」は、まさに状態の変更を目的としたものであるが、ここでいう破壊的処理は主目的かどうかを問わず何らかの状態の変更が行われてしまう処理を指す。
たとえばWhat表に書いた「単純な手続き」は多くの場合はセルへの書き込み等の破壊的処理を含むことになる。

これは耳慣れない言葉だと思うが、私はRubyの学習でこの言葉を知った。
破壊的という言葉の響きは何か悪いものを連想させるかもしれないが、全くそういった意味は無い。
大抵の状態変更は意図された正当なものであるが、一律「破壊的」と呼ばれる。

非破壊的処理

プロシージャ外部の状態変更を引き起こさない処理を指す。単純に状態の参照だけならそれは非破壊的処理である。
代表的なものにプロパティの参照や関数がある。

対話的処理

ユーザーに対してダイアログやフォームを表示させる処理をいう。
MsgBoxやInputBoxはFunctionで作られているが対話的処理の代表である。
個人的にはFunctionは純粋な関数に使用されてほしいが、Subで値の取得をするというのも微妙だしProperty Getに対話的処理を入れるなどもってのほかだと考えているので、値を返す対話的処理の場合は妥協してFunctionを使う。

プロシージャで複数の戻り値を扱えないことに対する代替手段

FunctionやProperty Getは単一の戻り値しか返せない。
そこで代替手段として引数を参照渡しにしておき、戻り値の代わりとするテクニックがよく用いられる。
これはこれで否定しないけれど、もしその複数の値が互いに関連するものだった場合は以下のいずれかの手段を採った方がデータの扱いが楽だ。

  • クラスモジュールでオブジェクト設計し、そのオブジェクトを返す。
  • コレクションに格納して返す。
  • 配列に格納して返す。

これらの方法では複数データを単一の戻り値として扱えるのでFunctionやProperty Getを使った非破壊的な処理が実現できる。
つまり複数値を扱いたいがために、表で紹介した原則を曲げるという必要がなくなる。

おわりに

今回紹介したように、プロシージャの使い分けに関して私は一応の指針を持っている。
ただ実際にはそれでもよく迷う。たとえば「状態の取得でかつ、重たい処理」をFunctionとProperty Getのどちらで実装しようか等。
連続で何度も呼び出されるならパフォーマンスに影響しかねないのでHowを優先してFunctionで作る。めったに参照されないならそこまでパフォーマンスに影響ないのでWhatを優先してProperty Getで作るなど。

また、表で×にしたものは、やめておいたほうが良いという私の意見であるが、やろうと思えばできてしまうし、どうしても×のものを選択した方が良いというケースもあるかもしれない。

プログラムが正常に動作する以上、絶対にダメということは無い。最終的にはケースバイケース。自分の頭で悩み・決断を繰り返すうちにコーディングスタイルが磨かれるはず。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。