処理を終了する

Sub・Functionプロシージャを強制的に終了させるコードとEndステートメントについて

'Subプロシージャを終了する
Exit Sub
'Functionプロシージャを終了する
Exit Function

Subプロシージャを終了させるには「Exit Sub」を使用し、Functionプロシージャを終了する場合は「Exit Function」を使用します

プロシージャの種類を「Exit」の後に記載すればいいだけですね

Exit Subの動きについて

ExitSubの動き
ExitSubの動き

画像の動きを確認してください
「Exit Sub」の動きですが、このコードが実行された時点でSubプロシージャは終了するので、「MsgBox」以下の処理は実行されていません

また、「End Sub」にも移動はしません、これと同じ意味になるためです

主には、処理の実行前に条件が整っているかを調べて整っていなければ終了させる場合に使用します

Exit FunctionとEndについて

ExitFunctionとEndの動き
Exit FunctionとEndの動き

画像の動きを少し、大きくしてみます
この画像であれば、「Exit Function」の動きが分かります

処理テストAにて、MsgBoxの本文引数として処理テストCのFunctionが呼び出されています
このFunctionプロシージャは、ただ「100」を返すだけの関数ですが、その代入前に「Exit」が実行されるためLong型の初期値「0」が返されてメッセージに表示されます

また処理テストBがその後、Callにより実行されています
この処理テストBには、例にはない「End」というコードが記載されています

この「End」というコードは、実行された時点でVBAの処理が全て強制終了するコードです
そのため、呼び出し元の処理テストAに戻らず、処理テストBの時点で終了しています

このコードが実行された時点で処理はもちろん、変数も全て解放され、メモリ内で開いているファイルも全て閉じられます
完全に処理が終了する形になります

さらに、このコードはイベントさえも実行させません
ユーザーフォームで「QueryClose」イベント(フォームの終了時に発生するイベント)を作成していても、そのイベントが実行されずに終了します

全てまっさらで、逆にせいせいするね
なんて事を言ってられない程、まっさらにしてしまうのでこのコードは恐いです
Excelの強制終了コードと同じレベルの強制力なので、使用することはほぼあり得ないと思いますが、使用するような場面に遭遇したら逃げ出してください
使用しないようになんとか頑張ってください、ここまで全ての終了をあいまいにさせることは非常に危険です

処理を終了させることも、変数を開放することも、開いたファイルを閉じることも専用のコードがちゃんとあります
そちらを使用して、しっかりそれぞれを終了させてください

それでも全く利用場面がない訳ではありません、利用場面の解説は以下の記事にあります

ループを終了する

For・Doの各ループ処理を強制的に終了させるコード

'Doループを終了する
Exit Do
'Forループを終了する
Exit For

ループ処理において、ループの中で終了条件を判定させて満たした時点でループを強制終了するには、「Exit」を使用します

ループは「Do」と「For」の2つなので、「Exit Do」「Exit For」のどちらかを使用します
(Doループはこちら - Forループはこちら)

ループの強制終了の動き
Exit使用時の動き

画像の様な動きになります

1つ目はDoループの強制終了の動きです、本来空白セルになるまでループしますが条件判定前にExitを使用することによりそこでループが終了します
「Loop」には移動せずに次のForループに移動しています

2つ目のForループも同様にExit使用時に「Next」移動せず終了します

画像とは違って、Doループで終了条件を指定せず処理の途中でIf分岐を使用して、この構文で終了させることも可能で、これが途中判定になります
途中判定を使う理由としては、ネストさせたIfやSelectCaseによる詳細な終了条件の指定が可能だという点になります
Unitlでは1つの条件式になってしまうので、それだけでは終了させられない場合に使用します

戻り値のあるプログラムの入れ物を作る

Functionプロシージャとは戻り値を持つプロシージャの事、使い方とコード

'戻り値のあるプログラムコードの入れ物
Function プログラム名() As Long

’↑この間にプログラムコードを入力する↓

’処理結果を代入する
プログラム名 = 100

End Function

戻り値のあるプログラムの作成には「Function」プロシージャを使用します

戻り値とFunctionプロシージャ

戻り値とは、処理の結果を呼び出し元に戻す結果の事です
要は関数の動きが同等のものとなります

例えば、Date関数は引数無しの関数です
「Date」とすれば、本日日付が返されます
この本日日付が戻り値と呼ばれます

Functionプロシージャでは、戻り値の代入先はプロシージャ名自身になりますので、例の内の「プログラム名 = 100」がそれに当たります
ここで、戻り値を取得させています
これが無いと戻り値は初期値のままになってしまいます

例のコードでは、プログラム内では最後に「100」を代入しているので常に「100」を返す関数になります

関数と同じという意味の通り、FunctionプロシージャはCallで呼び出さずに関数のように記載して実行できます

Functionプロシージャの動き
Functionプロシージャの動き

画像の動きを確認してみます

まずは、Subプロシージャによって関数の様にプログラム名のみで実行されます
今回はMsgBoxの文字として取得しています

このFunctionプロシージャは常に100を返すので、MsgBoxでは「200+100」が実行されて、300のメッセージが表示されます

またSubと違うのが、プロシージャ名の後にデータ型の指定を行う点です
今回は数値なのでLong型になっています
他の型も使用可能です

関数の便利さは実感出来ているはずですので、このFunctionプロシージャも使いこなすと非常に便利な機能なのが分かってきます

ユーザー定義関数とは

ユーザー定義関数の動き
ワークシート関数の様に使用可能

また、ユーザー定義関数というものの作成も行えます
セルに「=プログラム名()」と入力して確定させてみてください
すると数値の100が表示されます

これは関数と同じ動きなので、データの更新で再計算されます(この関数は意味ありませんが)
リアルタイムでの実行が可能になるので、関数を頑張って作成するよりはるかに楽に目的の関数を作成できたりします
なんなら、Functionプロシージャ内をワークシート関数だけで作成してしまって
本来ならネストされまくってる関数をたった1つの関数で実行してるようにも見せられます(こんなことをしても自己満足ですけどね~)

ただしループ処理やエラー対応にはしっかり準備してから使用してください
ユーザー定義関数でのバグは、Excelが強制終了することがあります

他のプロシージャを実行する

Callステートメントを使用することにより他のプロシージャが実行できます

'別の処理を実行する
Call プロシージャ名

現在実行中の処理中に、別の処理を実行するには「Call」を使用します
「Call ~~」の「~~」にプロシージャ名を指定することで、別の処理を実行できます

Callステートメントでの処理呼び出しの動き
Callで呼び出された時のコードの動き

画像の動きを確認してください(黄色い背景色が実行されている順番です)

処理テストAの中で呼び出しを行っています
呼び出された所から、処理テストBに移動して処理が実行されます
「End Sub」まで実行されたら、呼び出し元の処理テストAの続きが実行されます

主な使用場面は、同じ処理を連続して行ったり(「Call 処理テストB」を5回記載すれば5回実行される)、処理が複雑で長いコードを小分けにして別の場所に作成して処理を呼び出したりする際に使用します

なお、呼び出し先の処理で強制的に処理を終了させる方法もあります
例でいうと、処理テストBの途中で「End」を使用することで、処理テストAに戻らずに処理が終了します

ただこの動きは、処理テストAの終了があいまいになり非常に不安定要素となってしまうのでおすすめしません

定数の宣言方法

処理の実行中は変更される事の無い定数の使用方法と解説

'定数を宣言して値の代入を行う
Const パス名 As String = "ファイルパス文字列"

定数を使用するには「Const」を使用します

基本的な宣言の仕方は変数と同じで、定数名とその後にデータの型を宣言します
(文字列型変数の宣言方法はこちら)

定数とは、文字通り定まったデータです
宣言時にデータの代入を行い、代入後にデータの変更は出来ません

基本的には変数を変更しなければ同じ役割で対応できますが、使用する利点は何より宣言セクションで値の代入ができる点です
変数の場合はプロシージャの中でデータを代入しますが、定数はコンパイル時に代入されます

コンパイルとは、実際に処理が実行される前段階の事です

宣言セクションについて

定数の宣言方法と場所
赤枠内のプロシージャの外上が宣言セクション

このように宣言セクションで値を代入させることは、コードの可読性を高めます

例のような固定パスを定数とした場合に、フォルダ名の変更などによりパスが変更される可能性があります
これを変数でプロシージャ内の使用部分で宣言、代入をしていた場合にその位置を探すのが大変です

宣言セクションに定数で代入をしておけば、モジュール最上段にそのコードがあるので、パスの修正が必要な際に見つけやすくなります
これは、自分だけでなく他の人のためにも有用な方法です
優位性が薄まりますが、プロシージャ内に宣言することも変数同様に可能です

なお、定数は文字列や数値で使用しオブジェクトは使用できません
また、宣言セクションで変数に値を代入することは出来ません(宣言自体は可能です)

オブジェクトでループする(For Each)

オブジェクトに対して行うループ処理の解説

'A1からA10までのセルでループ
For Each オブジェクト変数 In Range("A1:A10")

'処理内容

Next オブジェクト変数

オブジェクトに対してのループには「For Each」を使用します

このオブジェクトに対してループというのは、初心者では難しい内容です
オブジェクトの操作自体が出来てきて、便利さが分かってくると確実に使う場面が出てきます
なにより、戻り値がオブジェクトというものもありそれに対応するには必要です

Forループでの解説同様にこれも変数とセットで使用します
その際の変数はObject型になります(この変数についてはこちら)

「Object」という型でもいいのですが、より読み取りやすくするために対象を絞った型宣言をおすすめします
今回のようにセルを代入するのであれば、「Range」を使用してください

変数の代入式には「In」を使用します
このループ処理でのオブジェクト変数にはA1~A10までのセルが順番に代入されます
一括で全て代入する通常の変数使用時と違い、1要素ずつなので「=」ではなく「In」が使用されます

A1~A10のそれぞれのセルが、「Next オブジェクト変数」の時点で次の要素が代入され、最後の要素が代入されて次の要素が無ければループは終了します
この際にオブジェクト変数は初期値の「Nothing」が代入されるので、通常の変数の解放操作は必要ありません

オブジェクトでループする理由は、まさにオブジェクトの持つ特性の利用です
オブジェクトにはそれぞれさまざまなプロパティが存在していますので、それらをまとめて参照できるようになります
オブジェクトループが出来れば、ループ処理はコンプリートです

数値でループする(For)

終了条件が確定している状態でのループ処理

'変数が0~10までループ、加算値は1
For 変数 = 0 To 10 Step 1

'処理内容

Next 変数

ループ処理には大きく分けて2つあり、「Do」と「For」です
Forループは数値を条件としてループさせるため、ループの回数が明確な場合に使用します

またForループでは変数にループ回数の数値を代入することから、Forループの使用にはセットで変数が必要です(変数の使用はこちらの記事になります)

「For 変数 = 0 To 10」の赤字範囲が開始と終了の条件になります
0から始まり10でループを終了します、その際に数値は変数に代入されます

「Step 1」の部分が数値の加算値になり、「0 → 1 → 2 ・・・」と続きます
なお、この「Step」と数値は省略でき、省略した場合は加算値が1になります
「For 変数 = 0 To 10」という書き方をすれば省略になります

ForループとDoループについて

内容によってはDoループで代用はできるので、変数がめんどくさい人はDoループでいいと思います

それでも、DoよりForを使用する利点

・変数に自動的に加算していってくれること(Nextの時に加算される)
・加算の幅も指定できること
・初期値を自動的に入力してくれること
・Doループより処理速度が速いこと

加算と幅の指定は、使ってみると結構楽になるのでいいですし、初期値の入力構文がいらないのが楽です
処理速度はほとんどの場合は誤差の範囲内かもしれませんが優位なのは確かです

どちらもいい部分がそれぞれにあるので、どちらかに固定して使う必要はありませんし優先順位も無いと思います
使いたい場面で使えるほうを使えばいいと思います

ループ処理-後判定-(Do Until)

処理を実行してから判定を行い条件が成り立つまでループするコード

'アクティブセルが10以上になるまで1を足す
Do
ActiveCell = ActiveCell + 1
Loop Until ActiveCell >= 10

通常のループ処理には「Do」ループを使用します
後判定を使用する場合は、最後の「Loop ~~」の「~~」に条件を指定します
Until以下条件式の解説は、前判定記事にあります

後判定とは、ループ処理を実行後に判定を行うものです
「後」なので、最初に判定は行われないため最初の1回は必ず実行されます

この例では、アクティブセルが9であった場合に1度実行するとアクティブセルが10になり処理終了、もう一度実行すると1回は無条件実行なので11になって終了します
前判定であれば加算処理は行われませんが、後判定では必ず1回は実行されるので加算され続けます、そこが違う点です

1回目が必ず実行されるという以外は前判定と変わりません
つまり2回目以降は後も前も関係は無いので「最初の1回を実行するかしないか」で前か後かを判断して作成してください

ループ処理-前判定-(Do Until)

前判定により条件が成り立つまで同じ処理を繰り返すループ処理のコード

'アクティブセルが10以上になるまで1を足す
Do Until ActiveCell >= 10
ActiveCell = ActiveCell + 1
Loop

ループ処理には、種類が複数あります
大きく分けると「For」と「Do」の2つがあり、それらが少し派生します

なかでも使いやすいのは「Do」の「Until」を使用する形です
「Until」はループの終了条件を指定するものです
条件式の書き方は他のコードと同じ書き方をし、このDoの後に条件式を書くことを前判定といいます(後判定はこちら)
Untilは条件式が真(True)になった時に終了します

これとは別に「While」という終了条件判定もありますが、こちらは終了の条件ではなくループを続ける条件を指定します
基本的にループ処理を作ることを考えたときに、終了条件の方が考えやすいと思うので「While」の説明は割愛します
条件式の作り方でどちらも同じ動きをするため、両方つかいこなす必要もありません

無限ループについて

ループ処理には常に付いて回るエラーが、無限ループです

たとえばこの記事の条件では10以上となっています
これを「Do Until ActiveCell = 10」として、セルの数値が11以上であった場合「12…13…14…」と加算されていき、永遠に10になることが無いので無限ループに陥ります

ループを作成した場合は、しっかりループが終了する事を確認してから処理の実行を行うようにしてください

もし無限ループに陥った場合は、「Ctrl+Break」か「Esc」で処理の実行をキャンセルさせるようにします(うまくいかない場合もあります)

ただ、この無限ループは意図せず発生した場合にエラーとして認識しますが通常は監視用無限ループというプログラミングもあります
なんらかの条件が成り立つまで待機するようなプログラムを作る際に使用します

ですが、VBAはプロシージャの複数同時実行は得意ではありません
1つのプロシージャを監視無限ループさせて、別のプロシージャで別の処理を並行して行うことは基本的に出来ません、というか安定しないので出来てもしないでください

この作りではデバッグもうまく機能しません、別のプロシージャに切り替えるようなデバッグの方法がありません

これを実現したい場合はVBAではなく、VBScriptという別のプログラミング言語がありますのでそちらを応用します

SelectCase条件分岐、逆条件

SelectCaseで答えの値から条件分岐をする方法

'条件を対象にして分岐
Select Case True
Case UserForm1.OptionButton1.Value

Case UserForm1.OptionButton2.Value

Case Else

End Select

SelectCase分岐の逆説的な使い方として、答えの方から分岐させる方法があります

よく使うのがユーザーフォームでオプションボタンを使用した際に、どれがチェックがついているかを判断することです

オプションボタンは、複数の中で1つだけONにできるコントロールです
オプションボタンはONの状態(チェックがついた状態)で「Value」プロパティが「True」になり、OFFなら「False」になります

そして、そのTrueを対象として各コントロールのValueプロパティを検証します
それが「Select Case True」の部分になります

この後の条件式には、対象とするコントロールを指定していきます
オプションボタンなら1つしかTrueにならないのでいいですが、チェックボックスなどで検証する場合は上にあるほど優先されることに注意します
逆にその優先度を利用することもいいかもしれません

OFF状態を検証したい場合は、対象を「False」とします
もちろん数値や文字列などを対象に検証することもできます