2019-12-29に更新

VBAをパーフェクトにマスターするシリーズ♪ 5章「プロシージャ」「パーフェクトExcel VBA」

■前回までの記事

VBAにもっと詳しくなろう♪ 「パーフェクトExcel VBA」

VBAをパーフェクトにマスターするシリーズ♪ 2章「式と値」「パーフェクトExcel VBA」

VBAをパーフェクトにマスターするシリーズ♪ 3章「ステートメント」「パーフェクトExcel VBA」

VBAをパーフェクトにマスターするシリーズ♪ 4章「フロー制御ステートメント」「パーフェクトExcel VBA」

続きを書きました。
VBAをパーフェクトにマスターするシリーズ♪
今回は「5章」で新しく知ったことと、思ったことを簡単にまとめました。

これは学習記録です。
書籍の内容は、あえて多くは載せていません。
大事なことも省いていますので、詳細につきましては、ぜひ書籍をご覧ください。

■5章「プロシージャ」手続きに名前を付ける

5-2  Subプロシージャ

5-2-2 Subプロシージャの呼び出し

[Call] name [argumentlist]

Callキーワードは省略可能。
但し、Callキーワードを書いたときは、引数リストの丸かっこが必要 Call name (argumentlist)
Callキーワードを省略したときは、引数リストの丸かっこが不要 name argumentlist

そんな違いがあるそうです。
しかも引数が1つの場合は、Callキーワードなしで丸かっこを付けてもエラーにならない。

結論:Callキーワードを書いたほうが見やすいので、常に書くようにしたい。

5-3 引数の構文

5-3-1 引数の指定項目

Sub name ([arglist])
[statements]
End Sub

▼引数の構文
[Optional] [ByVal|ByRef] [ParamArray] varname[()] [As type] [ = defaultvalue]

・ByVal、ByRefを省略すると、規定はByRefになる。

引数では、規定値(defaultvalue)を指定することもできるようです。知りませんでした。
以降で詳しく説明されています。

5-3-2 引数の順序と名前

Call SayHello ( name:="Bob", message:="Hello")

名前付きで引数を指定することもできる!

5-3-3 引数をオプションにする

Sub SayHello(message As String, Optional name As String = "Bob")

End Sub

・引数の構文の先頭にOptionalをつけると省略可能になる。
・その際に規定値を指定することができる(しないでもOK)。
・Optionalを付けた引数以降の引数は、すべてOptionalにしなければいけない。

・引数のデータ型がオブジェクト型の場合は、規定値にはNotingのみが設定可能だが、規定値を省略してもNotingになる。

5-3-4 値渡しと参照渡し

・ByVal、ByRefを省略すると、規定はByRefになる。

特に理由がなければByValキーワードを付与して値渡しにするほうがよいでしょう。

著者曰く、参照渡しには処理速度やメモリ容量の点でメリットもあるが、可読性や現在のPC性能を考えると値渡しのほうを推奨しますとのこと。

5-3-5 配列を引数として渡す

・引数として配列を渡すことができる。
・動的配列として受け取る方法とVariant型の変数として受け取る方法がある。

・動的配列の場合は参照渡しのみ可。(固定配列は渡せない。上限下限を指定できないため)

Sub Increment ( ByRef num() As Long)

End Sub

・Variant型の変数として受け取る場合、値渡しが可能となる。

Sub Increment ( ByVal num As Variant)

End Sub

5-3-6 パラメーター配列

パラメーター配列を使用すると、任意の数の引数を配列として受け取ることができます。

引数の数を固定せずに受け取れる機能。扱いに注意が必要。
いくつか条件が記載されています。

個人的には、特別な理由があるときに使うもので、敢えて進んで使うものではないように思います。

5-4 Functionプロシージャ

5-4-1 Functionプロシージャの定義

Functionプロシージャは、手続きを実行した結果戻り値を返すプロシージャです。

5-4-2 Functionプロシージャの呼び出し

Functionプロシージャを他のプロシージャから呼び出すには、戻り値を使用するかどうかで2つの構文を使い分ける必要があります。

戻り値を使用しないのならFunctionプロシージャにしなくて良いのでは?と思いましたが、
呼び出す側のプログラムが複数あって、使用したい場合とそうでない場合がある、ということであれば、そのような状況もありそうです。

えーと。

戻り値を受け取らずに、破棄しても良い場合は、Subプロシージャの呼び出しと同様、Callステートメントを使用することでFinctionプロシージャnameを呼び出すことができます。

[Call] name [argumentlist]

戻り値を受け取るようにFunctionプロシージャを使用する場合は、以下のようにFunctionプロシージャ名nameに続けて、引数リストaumumentlistを丸かっこで囲みます。引数を一つも渡さない場合は丸かっこは不要となります。

name [ ( argumentlist ) ]

このように、Functionプロシージャを呼び出す場合は、戻り値を使用するかどうかで引数リストを丸かっこで囲むかどうかが決まりますので注意が必要です。

えーと。。
今まで私はFunctionプロシージャを使うときは、戻り値を使用する前提で使っていたので、えーと、どういうことだろう、、、いらない場合があるのはわかるのですが、引数をかっこで囲まない? というのが少しややこしくて混乱しています。
「戻り値を使わない場合、Callで呼べるけど、引数をかっこで囲まない。」という決まりがある、ということですね。

5-4-3 Functionプロシージャから複数の値を返す

Functionプロシージャの戻り値は複数にすることはできません。したがって、複数の値を返したいというときには、ひと工夫が必要になります。

・Typeステートメントによるユーザー定義型を使って複数の値を返すことが可能。
・引数として複数の値を参照渡しで渡す。参照渡しなので値が変わる。

Typeステートメントを使ったことはありませんでした。後者が使われているコードは読んだことがありますが、モジュールレベル変数にしては?と思ったり、なぜSubではなくFuntionなのだろう?と思ったりしていました。

「戻り値を複数にする」という課題に対する手段は、多くの選択肢があり、これはVBAという言語の特徴をよく表しています。
つまりVBAは同じ目的に対して多くの選択肢が存在していて、その中からメリットとデメリットを踏まえて判断する能力が求められるということです。

5-5 Propertyプロシージャ

し、知らない。こんな機能があるなんて。

5-5-1 Propertyプロシージャとプロパティ

Propertyプロシージャは、クラスモジュールで使用されるものと言われることが多いですが、標準モジュールを始め、他のモジュールでも使用することができます。
詳しくは6章で紹介しますが、

本節では、標準モジュールに「プロパティ」を作成していく方法を見ていくことにしましょう。

5-5-2 Property Let/Setプロシージャの定義

Property LetプロシージャおよびProperty Setプロシージャは、モジュール変数に値またはオブジェクト参照を設定するためのプロシージャです。

5-5-3 Property Letプロシージャの呼び出し

変数はPrivateにしておいて、Property LetはPublicにしておくと、
Property Letを通るときに、変数に入る値をチェックすることができる。
適切でない値が変数に入らないようにすることが可能です。(カプセル化ですね。)

5-5-4 Property Setプロシージャの呼び出し

Letと同様に。

5-5-5 Property Getプロシージャの定義

Getは取得のほう。

5-5-6 Property Getプロシージャの呼び出し

name [(argumentlist)]

値を返すので、Callは不要。(だけどいきなり唐突に書いてあるので、コードをパッと見ると「何コレ?!」ってなりますね。

Property Getプロシージャは手続きを持つことができますので、モジュール変数に処理を加えて取り出すことができます。つまりモジュール変数を増やさずともプロパティを作ることができます。

便利です。

Private price_ As Long

Public Property Let Price(ByVal newPrice As Long)
    If newPrice >= 0 Then price_ = newPrice Else price_ = 0
End Property

Public Property Get Price() As Long
    Price = price_
End Property

Public Property Get TaxIncluded() As Currency
    Const TAX_RATE As Currency = 0.08
    TaxIncluded = price_ * (1 + TAX_RATE)
End Property

▼別モジュール

    Sub MySub()
        Price = 100
        Debug.Print Price, TaxIncluded
    End Sub

適切なプロシージャ化は、ただのコードの羅列についてその役割を浮き彫りにして、使いまわしがきくようにします。また、その守備範囲を線引きして、無関係な処理との関連性を下げます。

ある処理が存在するとき、それについてどのようなまとまりでプロシージャ化するのか、またどのようなプロシージャ名にするか、引数や戻り値の受け渡し方法はいかにするかなど常に考えを巡らせて、その判断力を磨き続けていきましょう。

これは理想的ですがとても難しいことだなーと思っています。
実務の場合、ある機能Aを作ったあとに、まったく別の機能Bを作り、次に機能Cを作るときになって、そういえばAの機能で作った箇所が部分的に使えるかも、となることはあります。

そのときに、機能Aを見直して、共通の処理を共通のモジュールに移動させることができればいいのですが、「大至急作る」ときにそこまでなかなかできず、結果的に機能Aと機能Cにそれぞれ別の同じ処理を書いてしまうこともあります。

もしくは機能Aの箇所に書いている処理を機能Cからも呼び出して使うのですが、そうすると今度は機能を減らすときに、コードを削るのが難しくなります。

リファクタリングは大事ですが時間と手間がかかります。
作る段階で全体像を見渡せればよいのですが、継ぎ足し継ぎ足し作っている場合は、
できるだけ、その都度コードの見直しができたらいいのでしょう。。
すくなくとも、後からでもわかりやすいように命名に気を付けようと思います。

今回こうしてまとめて学習して、こんな機能があったのか-知らなかった、ということも多かったです。
最初からすべてを理解して進めるのは難しいですが、
どこかのタイミングで改めて、総合的に学習することはとても大切な気がしています。

本書、まだ1/3にもたどり着いていません。先は長いですね・・・。

▼著者サイト

書籍「パーフェクトExcel VBA」発売についてのお知らせ

▼Amazonリンク

パーフェクトExcel VBA (PERFECT SERIES)   高橋 宣成著

ツイッターでシェア
みんなに共有、忘れないようにメモ

view_list [連載]「パーフェクトExcelVBA」の本で勉強中〜
第3回 VBAをパーフェクトにマスターするシリーズ♪ 3章「ステートメント」「パーフェクトExcel VBA」
第4回 VBAをパーフェクトにマスターするシリーズ♪ 4章「フロー制御ステートメント」「パーフェクトExcel VBA」
第5回 VBAをパーフェクトにマスターするシリーズ♪ 5章「プロシージャ」「パーフェクトExcel VBA」
第6回 VBAをパーフェクトにマスターするシリーズ♪ 6章-1「モジュール」「パーフェクトExcel VBA」
第7回 VBAをパーフェクトにマスターするシリーズ♪ 6章-2,3,4,5,6「モジュール」「パーフェクトExcel VBA」

Hata

個人でアプリを作ってたりなかったり(゚ω゚)。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント