VBA技術解説
VBAのVariant型について

ExcelマクロVBAの問題点と解決策、VBAの技術的解説
最終更新日:2020-07-05

VBAのVariant型について


Variantデータ型は、他の何らかのデータ型として明示的に宣言されていない変数で、全てのデータ型を入れることができます。
Variantデータ型には型宣言文字はありません。


Variant型は、特別な値 Empty、Error、Nothing、Null を格納することもできます。
ただし、固定長文字列データ型として格納することはできません。
また、ユーザー定義型を格納することもできません。

Variantデータ型を他の具体的なデータ型の代わりに使用することにより、データをより柔軟に処理できます。
データ型の基本については以下を参照してください。
変数宣言のDimとデータ型|VBA入門
マクロVBA入門者が、まず最初につまずくのが、このDimで変数を宣言することでしょう。変数とは、数値や文字列など(すなわちデータ)を一時的に格納する入れ物 と良く説明されますが、まずはこの考えで良いでしょう。この入れ物には、いろいろなタイプの入れ物があります。
定数宣言のConstと型宣言文字|VBA入門
前回は変数を説明しましたが、変数があれば、当然のように定数もあります。変数は値が変わる(変えられる)もの、定数は値が変わらない(変えられない)ものです。定数とは、数値や文字列の代わりに使用される、意味を持たせた名前のことです。

目次

Variant型変数のデータ型を調べる

Variant型変数をローカルウィンドウでみると、Variant/の後に実際に入っているデータの型が表示されます。
VBE(VisualBasicEditor)は、VBAで使われるコードエディタ、コンパイラ、デバッガ、その他の開発支援ツールが統合された開発環境です。ここではローカルウィンドウの使い方について説明します。ローカルウィンドウの表示 メニュー「表示」→「イミィディエイトウィンドウ」初期設定では、コードウィンドウの下にドッキング表示されます。

VBA マクロ Variant

Variant型変数のデータ型をVBAで判定する場合は、VarType関数またはTypeName関数を使用します。

VarType関数は、引数に指定された変数の内部処理形式を表す整数型(Integer)の値を返します。
VarType関数は、引数に指定された変数の内部処理形式を表す整数型(Integer)の値を返します。VarType関数 VarType(varname) varname 必ず指定します。引数varnameには、ユーザー定義型の変数を除く、任意のバリアント型(Variant)の変数を指定します。
TypeName関数は、引数で指定された変数に関する情報を文字列で返します。
TypeName関数は、引数で指定された変数に関する情報を文字列で返します。TypeName関数 TypeName(varname) varname 必ず指定します。引数varnameには、ユーザー定義型の変数を除く、任意のバリアント型(Variant)の変数を指定します。

バリアント型 (Variant) 変数の初期値はEmptyです。
変数がEmpty値であるかの判定には、IsEmpty関数を使用してください。
IsEmpty関数は、変数がEmpty値かどうかを調べ、結果をブール型(True,False)で返します。IsEmpty関数 IsEmpty(expression) expression 必ず指定します。引数expressionには、数式または文字列式を含むバリアント型(Variant)の式を指定します。

Variant型変数のメモリアドレス

変数のアドレス位置にはデータ型を区別する数値が入っています。
詳しくは以下を参照してください。
VBAにおける変数のメモリアドレスについて
VBA開発においてメモリアドレスを気にすることはほとんど無いと思います。気になる場合があるとしたら、・String変数の処理が遅い ・Variant変数の処理が遅い ・ByRef,ByValの違い ・WindowsAPI使用時 このような場合に多少は気になる事があるくらいではないでしょうか。

Variant型どうしの算術演算

Byte、Integer、Long、Singleを格納しているVariant対して算術演算を実行し、結果が元のデータ型の通常の範囲を超えた場合、結果は Variant内で次に大きいデータ型に昇格されます。
ByteはIntegerへ、IntegerはLongへ、LongとSingleはDoubleへ昇格されます。

Dim v1, v2, v3, v4, v5, v6, v7
v1 = CByte(125)
v2 = CByte(125)
v3 = v1 + v2
v4 = v1 * v2
v5 = v1 * v2 * 10
v6 = v1 ^ v2
v7 = v1 / v2
Stop
VBA マクロ Variant

Currency、Decimal、Doubleの値が格納されているVariant変数がそれぞれの範囲を超えると、エラーが発生します。

Variant型どうしの比較演算

バリアントの基になる型に応じて、式の比較方法または比較の結果が変わります。

条件 Then
両方のバリアント型 (Variant) の式が数値 数値比較を実行します。
両方のバリアント型 (Variant) の式が文字列 文字列比較を実行します。
一方のバリアント型 (Variant) の式が数値で、もう一方が文字列 数値式は文字列式よりも小さくなります。
一方のバリアント型 (Variant) の式がEmptyで、もう一方は数値 0をEmpty式として使用して、数値比較を実行します。
一方のバリアント型 (Variant) の式がEmptyで、もう一方が文字列 長さ0の文字列 ("") をEmpty式として使用して、文字列比較を実行します。
両方のバリアント型 (Variant) の式がEmpty 式は等しくなります。

最も注意すべき点は、一方が数値で、もう一方が文字列の場合です。
数値式は文字列式よりも小さくなります。

Dim v1, v2
v1 = 123
v2 = "120"
Debug.Print v1 < v2
Debug.Print v1 = v2
Debug.Print v1 > v2

上記VBAにおいては、実際の数値に関係なく常に、
True ・・・ 左辺が数値式なのでTrueになります。
False
False
このように数値式は常に小さく判定されます。

RangeのValueプロパティの比較

RangeのValueプロパティはVariantです。
従ってVariantの比較になるので注意が必要です。

VBA マクロ Variant
A1は数値、A2は文字列になっています。

VBA マクロ Variant

Dim v
v = "123"
Debug.Print Range("A1").Value = Range("A2").Value
Debug.Print Range("A1").Value = v
Debug.Print Range("A2").Value = v

数値式は文字列式よりも小さくなりますので、この結果は、
False
False
True ・・・ 文字列式どうしの比較
となります。

つまり、Valueは単純比較できません。
文字列を数値変換するには、
CLng関数
CLng関数は、引数をLong型(長整数型)に変換します。CLng関数 CLng(expression) 引数expressionには任意の文字列式または数式を指定します。この引数は必ず指定します。引数がLong型(長整数型)に変換できない場合はエラーとなります。
CDbl関数
CDbl関数は、引数をDouble型(倍精度浮動小数点数型)に変換します。CDbl関数 CDbl(expression) 引数expressionには任意の文字列式または数式を指定します。この引数は必ず指定します。
これらを使いますが、そもそも数値に変換できない場合はこれらの関数がエラーとなってしまいます。
そこで、事前に数値変換可能かを、IsNumeric関数を使い判定しておきます。
IsNumeric関数は、式が数値として評価できるかどうかを調べ、結果をブール型(True,False)で返します。IsNumeric関数 IsNumeric(expression) expression 必ず指定します。引数expressionには、数式または文字列式を含むバリアント型(Variant)の式を指定します。
さらに、そもそもエラー値の判定は、IsError関数で行う必要があります。
引数の式がエラー値かどうかを示すブール型(Boolean)の値を返します。IsError関数は、引数の式がエラーを示す場合にTrueを返します。それ以外の場合は、Falseを返します。IsError関数は、数式がエラーを表しているかどうかを判断するために使用されます。

Sub test()
  Debug.Print CompareValue(Range("A1"), Range("A2"))
End Sub

Function CompareValue(rng1 As Range, rng2 As Range)
  'エラー値の場合はFalseを返す
  If IsError(rng1.Value) Or IsError(rng2.Value) Then
    CompareValue = False
    Exit Function
  End If
  
  '両方数値の場合は数値比較する
  If IsNumeric(rng1.Value) And IsNumeric(rng2.Value) Then
    If CDbl(rng1.Value) = CDbl(rng2.Value) Then
      CompareValue = True
    Else
      CompareValue = False
    End If
    Exit Function
  End If
  
  '文字列比較
  If rng1.Value = rng2.Value Then
    CompareValue = True
  Else
    CompareValue = False
  End If
End Function

ユーザー定義型(Type)とバリアント(Variant)変数

ユーザー定義型をバリアント(Variant)変数に代入するとエラーとなります。

VBA マクロ Variant

VBAリファレンスには、
(Variant 型はユーザー定義型をサポートするようになりました)。
このように書かれていますが、
これは、ActiveXプロジェクトやタイプライブラリの構造体の事をさしていると思われます。
VBA内で定義した構造体を、バリアント(Variant)変数に代入することはできません。

Variant型の配列

通常の配列には、同じデータ型しか入れることはできません。

Dim n(1 To 3) As Long

この変数nの各要素には、数値または数値変換可能な文字しか代入できません。
しかし、Variant型の場合は、任意のデータ型を入れることができます。

Dim n(1 To 3) As Variant
n(1) = 123
n(2) = 1.23
n(3) = "ABC"
Stop

VBA マクロ Variant

配列をVariant型の仮引数に渡す

配列を引数に指定する場合は、ByRef(参照渡し)が強制されます。
ByValで配列を指定した場合は、赤字のコンパイルエラーとなります。

VBA マクロ Variant

しかし、仮引数をVariantで直接受け取る場合はByValで受け取ることができます。

Sub test()
  Dim str(2) As String
  str(0) = "A": str(1) = "B": str(2) = "C"
  Dim v
  v = fnc(str)
  Stop
End Sub

Function fnc(ByVal v) As Variant
  v(0) = "Z"
  fnc = v
End Function

VBA マクロ Variant

RangeオブジェクトをVariant型の仮引数に渡す

RangeオブジェクトをVariantの仮引数に渡した場合の挙動については注意が必要です。

ByRef、ByVal、どちらで受け取っても受け取った時点ではRangeオブジェクトです。
しかし、仮引数に何かを代入した時点で違いが出てきます。

ByVal、ByRefの基本については以下を参照してください。
値渡し(ByVal)、参照渡し(ByRef)について
ByValが値渡し、ByRefが参照渡しです。ここまでは、どこにでも書いてあります。しかし、なんとなく理解できるけど、なんとなく理解できない… 結局のところ実際の活用がなかなか出来ない事が多いようです。

Sub sample()
  Dim rng1 As Range
  Dim rng2 As Range
  Set rng1 = Range("A1:B1")
  Set rng2 = Range("A2:B2")
  Call sample_sub(rng1, rng2)
End Sub

Sub sample_sub(ByRef v1, ByVal v2)
  Stop '①
  v1 = 1
  v2 = 1
  Stop '②
End Sub

①の時点:受け取った直後

v1、v2、どちらもRangeオブジェクトとして受け取ります。

VBA マクロ Variant

②の時点:代入した後

ByRefで受け取ったv1は、Let代入ではValueに入ります。
ByValで受け取ったv2は、Let代入では変数が置き換わります。

VBA マクロ Variant

ただし、当然ではありますが、
Set代入した場合は、代入したオブジェクトになります。

Valueの配列で渡したい場合

呼び出す時に配列で渡すようにします。
Call sample_sub(rng1, rng2)
これを
Call sample_sub(rng1.Value, rng2.Value)
このように、Valueプロパティを指定すればValueの配列で渡せます。
また、
Call sample_sub((rng1), (rng2))
このように、()での値評価をすれば、その時点で既定プロパティのValueが取得されるので、Valueの配列で渡せます。
ただし、実引数にCVar(…)としても配列になりません。



同じテーマ「マクロVBA技術解説」の記事

手動計算時の注意点と再計算方法
VBAの用語について:ステートメントとは
オブジェクト変数とは何か
VBAの少数以下の演算誤差について
スピルでVBAの何が変わったか
CharactersプロパティとCharactersオブジェクト
ユーザーに絶対に停止させたくない場合のVBA設定
印刷範囲の設定・印刷範囲のクリア
VBAの省略可能な記述について
VBAのVariant型について
VBAのインデントについて


新着記事NEW ・・・新着記事一覧を見る

タプル(tuple型、イミュータブル)|Python入門(9月15日)
リスト(list型、配列)|Python入門(9月14日)
文字列操作(str型)|Python入門(9月13日)
greeenはgreenに、greeeeeNをGReeeeNに変換|エクセル雑感(9月13日)
while文とデバッグ(ステップイン)|Python入門(9月12日)
入力規則で○△を入れる数を制限する方法|エクセル雑感(9月12日)
コレクションの要素を削除する場合|エクセル雑感(9月11日)
for文とイテラブルオブジェクト|Python入門(9月10日)
if文とインデントによるブロック|Python入門(9月9日)
ショートカット(Ctrl+Shift+n)抜け番ばどれだ|エクセル雑感(9月8日)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.RangeとCellsの使い方|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.マクロって何?VBAって何?|VBA入門
5.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
6.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
7.繰り返し処理(For Next)|VBA入門
8.セルに文字を入れるとは(Range,Value)|VBA入門
9.とにかく書いてみよう(Sub,End Sub)|VBA入門
10.マクロはどこに書くの(VBEの起動)|VBA入門




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。



このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ