和風スパゲティのレシピ

日本語でコーディングするExcelVBA

Function と Property Get の違い

プロシージャの種類である、「Functionプロシージャ」と「Property Getプロシージャ」の違いを解説します。

同じところ

両者ともプロシージャであり、いわゆる(広い意味での)関数です。

どちらのプロシージャも、任意の数(0でもよい)の引数を受け取り、1つの値を返します。

Function 足し算(x As Long, y As Long) As Long
    足し算 = x + y
End Function

Property Get 足し算(x As Long, y As Long) As Long
    足し算 = x + y
End Property

このように「2つの値を受け取り、足したものを返す」プロシージャは、
↑のどちらの書き方でもほぼ同じ動きをします。


Function と Property Get の違いを調べている方は、
この点に疑問を抱かれたのかもしれませんね。

あれ?同じじゃね?って


ちなみに

  • Function は返り値を無しにできる
  • Property Get はクラスモジュールで使う

いずれも誤りですのでご注意ください。


Functionの返り値を書かない場合は、Variantが返ります。
返り値の型が省略されたとみなされるだけなので、

Function 足し算(x As Long, y As Long)
    足し算 = x + y
End Function

これでもちゃんと足し算した結果が返ります。


またProperty Get は先ほどの足し算のように、
普通に標準モジュールに書けます。

Propertyプロシージャの仕組みが、クラスの「カプセル化・隠蔽」を行う役割を担うため、クラスモジュールで使われることが多いというだけです。

違うところ

そもそものPropertyプロシージャの目的である、
「同じ名前のProperty Let/Set プロシージャが用意できる」
点が最も大きな違いです。

Property Get A1セルの値() As Long
    A1セルの値 = Range("A1").Value
End Property

Property Let A1セルの値(設定値 As Long)
    Range("A1").Value = 設定値
End Property

こんな風に「A1セルの値」という名前のプロシージャを2つ作ることができ、

A1セルの値 = 1
Debug.Print A1セルの値 ' =1

と、あたかも変数を扱っているかのような記述ができます。
まさに「プロパティ」を表現するためにあるのが、Propertyプロシージャです。


超簡単な例ですが、これでも「A1セルをLong型に限定して使用」する仕組みにはできていますね。

このプロシージャを無視して普通にA1セルをいじれてしまうため、隠蔽と呼ぶにはあまりにザルですが(笑)


この相違点を見ると、
「Property Let を作るときに使うのが、Property Get ってことか!」
と思いたくなりますが、そうじゃないのが難しいところです。


このA1セルを「値を見に行ってもいいけど、編集は許さん」というときは、
Property Let を用意しないという手法が取られます。

いわゆる読み取り専用プロパティですね。


さてここで最初の問題に戻ってきてしまいます。
Property Letを用意しないとき、

Property Get A1セルの値() As Long
    A1セルの値 = Range("A1").Value
End Property

Function A1セルの値() As Long
    A1セルの値 = Range("A1").Value
End Function

これは何が違うの?と。


この2つはシステム的な違いも多少はあるのですが、それよりも、
「人が読む時にどう感じるかの違い」と解釈するとスッキリすると思います。


つまりは、

Dim 消費税 As Double: 消費税 = 0.1
Const 消費税 = 0.1

これの何が違うの?と一緒の話なんですね。


値を変更しないのであれば、↑は同じものと言ってもいいです。
システム的には同じ動きをします。


でも人間が見た時は、「これは変更するつもりがないよ」と明示する点で、定数にする意味がありますよね。


これと同じことなので、言ってしまえば、
「Let/SetのないProperty Get とFunctionの違いは、作者の気持ち」です。

作った人がプロパティだと思っているかどうかが違います。

使い分け

Let/Setを作るときは、当然Property Getを使います。

Let/Setを作らないときは、「プロパティだと思ったら」Property Get を使います。
逆に「関数やメソッドだと思ったら」Functionを使ってください。



そもそも「プロパティ」って何だよ!


と言われる気がしますが、これが難しくてですね。


プログラミングの世界における、概念的、ニュアンスの話なので、
「これがプロパティだ!」とはなかなか説明ができません。

ひとまず直訳すると、性質・状態・属性・設定がプロパティです。


分かりやすい極端な例から言うと、

  • 「シートのデータが空か調べる」ならProperty Get
  • 「シートのデータが空ならシートを削除する」ならFunction

を使った方がいいです。


結果的にBooleanを返すことが同じだったとしても、
特に後者でProperty Getを使うのはマズいです。


部下に「倉庫に在庫あるか確認してきて」と頼んだら、
「倉庫が空だったんで爆破しておきました」
って言われた印象を受けます。


確認するだけ、今どうなってるか見るだけの時に限定してProperty Getを使った方が、誤解なく読み手に伝わると思います。

対して、何かの値を書き換えたり、オブジェクトを操作したりする場合は、
Function(またはSub)をつかってください。



では、上記の2例の中間っぽい、

Function 足し算(x As Long, y As Long)
    足し算 = x + y
End Function

これはどうですかね?

大体の人が関数(Function)っぽいと答えそうです。


でも、整数論を修めた数学者にとっては、
「1と2が足して3であることは数論の性質でありプロパティ」
って言うかもしれません。

別にそれは間違ってないと思います。

最終的には作者の気持ちです。


「使い分け」の答えになっていない答えですが、

あなたの心でプロパティと定義されているなら、Property Getを使ってください。

おまけ:細かい仕様の違い

先ほど「定数と変数は、値を変えなければシステム上は同じもの」と説明しました。


では値を変えようとするとどうなるかというと、

消費税 = 0.03

この時に違いが出ますね。

定数では、エラーメッセージ

定数には値を代入できません

が表示されます。


これと同じで、

○○ A1セルの値() As Long
    A1セルの値 = Range("A1").Value
End ○○

A1セルの値 = 1

を記述した際、


Functionで書いている場合は

代入式の左辺の関数呼び出しは、バリアント型またはオブジェクト型の値を返さなければなりません。

と、いわゆる「Function左辺に書くなよ警告」が表示されます。


対してProperty Get でこれをやると、

値の取得のみ可能なプロパティに、値を設定することはできません。

と、まさに読み取り専用プロパティであることの警告が出ます。


作者の気持ちがちゃんと反映されていますね。



なお、「Function左辺に書くなよ警告」が、オブジェクトならOKっぽい警告内容なのは、返り値のオブジェクトの、規定のプロパティへの代入を書くならOKだからです。

つまり、

Function A1セル() As Range
    Set A1セル = Range("A1")
End Function

A1セル = 1

この場合は、
「Functionが返したRangeオブジェクト」の
「省略時の既定プロパティであるValueプロパティ」
に1を代入していますので、エラーは出ません。


これはProperty Getでも同じであり、

Property Get A1セル() As Range
    Set A1セル = Range("A1")
End Property

A1セル = 1

これも「値の取得のみ~」メッセージは出ず、A1に1を代入できます。
当たり前っちゃ当たり前ですが、オブジェクト自体を読み取り専用にはできません。



最後に些細な違いをひとつ。

Property Get は「Call」できません

Functionは返り値を使わない場合に「Call」で呼び出せますが、
Property Get は値の取得のためのプロシージャであるためこれが許されず、
「Call」した場合は「プロパティの使い方が不正です」エラーになります。


まあ、↑で書きました通り、そもそも

「値がなくてもCallしたくなるような、何らかの変更を及ぼす処理」

をProperty Get にしてはいけません。


この延長で、別のブックからマクロを起動する際、
Application.RunメソッドでFunctionを呼ぶことはできますが、
Property Getは呼ぶことができません

まあめったに気にする部分ではないと思いますが、
Callと違い、Runは返り値を受け取ることもできるメソッドなので、
Property Get がダメなのがちょっと困る場面もあるかもしれません。

その時はプロパティっぽくても、Functionにしてください。