TL;DR —
Propertyは、クラスのフィールドを ゲートに変えます。呼び出し側から見ればemp.Salary = 65000は素の変数のように 見えますが、その代入の裏であなたのコードが走ります — だから検証したり、計算したり、 書き込みを拒否したりできます。Property Getは読み取りのゲート、Property Letは 値を受け取り、Property Setはオブジェクトを受け取ります。LetとSetの 取り違えがナンバーワンのエラーで、Letをわざと省くのがフィールドを読み取り専用に する方法です。
' --- クラスモジュール(clsEmployee)の内側 ---
Private mSalary As Currency ' バッキングフィールド、外部からは隠されている
Public Property Get Salary() As Currency ' 読み取りのゲート
Salary = mSalary
End Property
Public Property Let Salary(ByVal value As Currency) ' 値を書き込むゲート
If value < 0 Then Err.Raise 5, , "Salary cannot be negative"
mSalary = value ' 格納する前に検証する
End Property
' --- 使い方は素のフィールドとまったく同じに読める ---
' emp.Salary = 65000 -> Property Let を実行(検証して格納)
' Debug.Print emp.Salary -> Property Get を実行(mSalary を返す)
' emp.Salary = -100 -> "Salary cannot be negative" を発生させる
クラスモジュールは、データを素の Public 変数として
公開できます — そして、それが正しい判断であることもあります。しかし、クラスが公開
フィールドの詰め合わせよりも堅牢に感じられる理由は Property プロシージャです。各
フィールドに対して外の世界に何を許すかを、あなたが決めるためのフックだからです。
この記事で学べること
- 考え方の軸 — 生の変数ではなく、フィールドの前のゲート
- なぜ Property が素の Public 変数に勝るのか
- Get・Let・Set — そして誰もがつまずく唯一の区別
- すべてを機能させるバッキングフィールドのパターン
- 読み取り専用プロパティの作り方(そして、それがなぜ長所なのか)
- 素の Public 変数こそが正直な選択になる場面
考え方の軸:Property はフィールドの前に立つ「ゲート」
Property はフィールドの前に立つゲートです。あなたのオブジェクトを使う人に
とっては、通常の変数とまったく同じに見えます — emp.Salary = 65000 と書き、
x = emp.Salary と読みます — しかし、そのひとつひとつの接触が、あなたの書いた小さな
プロシージャを走らせます。読み取りは Property Get を、書き込みは Property Let
(値の場合)または Property Set(オブジェクトの場合)を走らせます。
その間接性こそがすべての要点です。あなたのコードがゲートの中に座っているので、格納 する前に値をチェックしたり、まったく格納しない値を計算したり、あるいは単に書き込みの ゲートを用意せずフィールドを読み取り専用にしたりできます。呼び出し側は知る必要が ありません — どちらにせよ構文は同じだからです。
なぜ Property が素の Public 変数に勝るのか
Public フィールドは、あなたのオブジェクトへまっすぐ通じた穴です。誰でも、いつでも、
何のチェックもなく、何でも書き込めます。emp.Age = -5 は成功します。emp.Id = 999 は、
恒久的であるはずだったアイデンティティを上書きします。ルールを置く場所がありません。
呼び出し側とフィールドの間にコードがないからです。
Property は、その場所を与えてくれます。それが防ぐ失敗は、無言の不正な書き込みです —
負の年齢、あとで計算を吹き飛ばす文字列にセットされた給与、こっそり再割り当てされた
「一意の」ID。Property Let があれば、不正な書き込みは、3 つ先の手続きで謎の間違った
結果として表面化する代わりに、起きたその場で、明快なメッセージとともに捕まえ
られます。
Get・Let・Set:誰もがつまずく唯一の区別
プロパティのプロシージャは 3 つあり、そのうち書き込みの 2 つの間の分かれ目が、VBA の 古典的なつまずきの石です。
| プロシージャ | 方向 | 受け取る/返す | 用途 |
|---|---|---|---|
Property Get |
読み取り | 値またはオブジェクトを返す | どのプロパティの読み取りにも |
Property Let |
書き込み | 値を受け取る(Long、String、Date、Currency…) | 値プロパティの書き込み |
Property Set |
書き込み | オブジェクトを受け取る(Range、クラスのインスタンス…) | オブジェクトプロパティの書き込み |
ルールは、VBA における通常の代入を支配するのと同じものです。
値は Let、オブジェクトは Set。 あるプロパティが Range を保持するのに、それに
対して Property Let を書いてしまうと、emp.HomeCell = Range("A1") の代入は失敗
します。オブジェクトの代入には Set が必要なのに — VBA は見つからない Property Set を
探すからです。プロシージャを、フィールドが保持するものの種類に合わせましょう。
Private mRange As Range
Public Property Get HomeCell() As Range
Set HomeCell = mRange ' 注意:オブジェクトを返すので Set
End Property
Public Property Set HomeCell(ByVal r As Range) ' Let ではなく Set — オブジェクトだから
Set mRange = r
End Property
' 使い方: Set emp.HomeCell = Range("A1")
Property Get の内側でさえ、オブジェクトを返すときは Set HomeCell = ... を使うことに
注目してください。Let/Set の区別は、どこであってもデータの種類に従います。
バッキングフィールドのパターン
プロパティはほとんどの場合、隠されたフィールドを包む対で現れます。フィールドは
Private で(m の接頭辞は「member(メンバー)」の慣習です)、外の世界はゲートを通して
のみそこに届きます。
Private mName As String ' 隠されたバッキングフィールド
Public Property Get Name() As String
Name = mName
End Property
Public Property Let Name(ByVal value As String)
mName = Trim$(value) ' 入ってくる途中で正規化する
End Property
これが、カプセル化を現実のものにする形です。データは 1 つの Private 変数の中に住み、
あらゆる読み書きは、あなたが制御するコードを通ります。名前の格納方法をあとで変えても —
トリミングする、大文字にする、リストと照合して検証する — 変えるのは 1 つの
Property Let であって、すべての呼び出し側ではありません。
読み取り専用プロパティの作り方(そして、それがなぜ長所なのか)
Property Let を省くと、フィールドは外部からは読み取り専用になります。クラスは
内部でバッキングフィールドを設定できますが、呼び出し側は読み取ることしかできません。
Private mId As Long
Public Property Get Id() As Long ' Get のみ — Let なし
Id = mId
End Property
Friend Sub AssignId(ByVal newId As Long) ' クラスが内部で設定する
mId = newId
End Sub
' emp.Id -> 動作する(読み取り)
' emp.Id = 42 -> コンパイルエラー:読み取り専用プロパティには代入できません
そのコンパイルエラーは障害ではありません — 保証です。読み取り専用プロパティは、 「この値は一度だけ設定され、二度と編集されない」を表現する方法であり、コンパイラーが あなたのためにそれを強制します。同じ手法は、バッキングフィールドをまったく持たない 計算プロパティも与えてくれます。
Public Property Get FullName() As String
FullName = mFirst & " " & mLast ' 導出される、何も格納しない
End Property
素の Public 変数こそが正直な選択になる場面
ここに、良い VBA を、意味も分からず真似た定型コードと分けるものがあります。すべての
フィールドに Property が要るわけではありません。 あるフィールドが検証を必要とせず、
計算される必要もなく、自由に読み書きしてかまわないなら、Public 変数はまさにそれを
言い表します — そして、mX を行ったり来たりコピーするだけの素通しの Get/Let の
対で包むのは、安全性ではなく雑音を足すだけです。
ですから、Property に手を伸ばすのは、理由があるときです — 検証、読み取り専用の
ID、計算される値、Set が必要なオブジェクトフィールド。素の、制約のない値には、
Public 変数が正直で読みやすい選択です。ゲートが必要なときにゲートを足しましょう。
開いたフィールドの周りにゲートを建ててはいけません。
ExcelMaster の活用
プロパティのルールは小さいけれど容赦がありません。値には Let、オブジェクトには
Set、読み取り専用には Let を省く、各対の裏には Private のバッキングフィールド。
Let/Set の組み合わせを間違えればコンパイルできませんし、カプセル化を間違えれば、
「保護された」はずのフィールドは丸開きです。
ExcelMaster は、
その形全体をあなたのために書きます。オブジェクトを説明すれば — 「給与を負にできず、
読み取り専用の ID と計算されるフルネームを持つ Employee」— Private のバッキング
フィールド、検証する Property Let、必要なところにはオブジェクト対応の Property Set、
そして Get のみの読み取り専用プロパティを、すべて正しく対にして生成します。どの
キーワードがどの種類のフィールドと組むのかを暗記しなくても、きちんとしたカプセル化が
手に入ります。
よくある質問
VBA の Property とは何ですか?
Property は、フィールドへのアクセスを制御するクラスモジュール内の
プロシージャです。Property Get はフィールドが読み取られるときに実行され、
Property Let(値の場合)または Property Set(オブジェクトの場合)は書き込まれる
ときに実行されます。呼び出し側には通常の変数のように見えますが — emp.Salary = 65000 —
代入の裏であなたのコードが走るので、検証したり、計算したり、フィールドを読み取り専用に
したりできます。
Property Let と Property Set の違いは何ですか?
Property Let は値を受け取ります — Long、String、Date、Currency など。
Property Set はオブジェクトを受け取ります — Range、クラスのインスタンス、あるいは
任意の参照型。これは通常の代入と同じルールです。値は =(Let 経由)を、オブジェクトは
Set(Set 経由)を使います。オブジェクトプロパティに Let を、値に Set を使うと、
コンパイルエラーか実行時エラーになります。
VBA で読み取り専用プロパティを作るには?
Property Get を書き、Property Let(または Set)は書きません。そうすると呼び出し側は
値を読めますが、代入はできません — 書き込もうとすると、コンパイルエラー「読み取り専用
プロパティには代入できません」になります。クラス自体は、なお内部でバッキングフィールドを
変更できます。これは、一度だけ設定される ID 値や、他のフィールドから導出されるフルネームの
ような計算プロパティに最適です。
Public 変数ではなく Property を使うのはなぜですか?
Public 変数は、いつでも、何のチェックもなく、どんな値でも書き込めます。Property は、
入力を検証し、値を計算し、フィールドを読み取り専用にし、あるいは呼び出し側に触れずに
あとでデータの格納方法を変える場所を与えてくれます。そうした理由があるときは
Property を使いましょう。素の制約のない値には、Public 変数の方が単純で、まったくもって
正直です。
VBA で Property Get は何をしますか?
Property Get はプロパティの読み取り側です。コードがそのプロパティを読むたびに
実行され、値を返します(オブジェクトプロパティの場合は Set で返します)。ふつうは
書き込み用の Property Let または Set と対にされ、Private のバッキングフィールドを
包みます — しかし Property Get を単独で置けば、読み取り専用または計算プロパティに
なります。
検証環境
検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-04.
関連ガイド: VBA Class Module · VBA Type · VBA Dim · VBA Function · VBA ByRef vs ByVal
