更新日: 2010 年 1 月 29 日

執筆者: エディフィストラーニング株式会社 矢嶋 聡

この記事は、「MSDN プログラミング シリーズ」として発行している技術書籍「ステップアップ Visual Basic 2010 ~開発者がもう一歩上達するための必読アドバイス」(日経 BP 社刊) を基に先進的なテクニックを紹介しています。


目次

  1. はじめに
  2. Variant 型の様々な用途例と VB.NET での代替
  3. 引数にあらゆるデータ型に対応できるように Variant 型にすると ...

1. はじめに

Visual Basic 6.0 までの Visual Basic 開発者にとって、多くの戸惑いを与えるのが、ここでご紹介する “Variant” です。従来の Visual Basic 6.0 までは、Variant 型と呼ばれるデータ型があり、このデータ型には、整数、文字列、オブジェクトなど様々なデータが代入でき、汎用性の高いデータ型として使用できました。しかし、.NET 版の Visual Basic では Variant 型をサポートしていません。一見すると、このデータ型が無くなって不便に感じるかもしれません。

そこで今回は、この Variant 型について、表題にも挙げた点も踏まえて、Variant 型の用途を改めて見極め、Variant 型が無くなったことで不都合はないのか、.NET での代替はあるのかなどをいくつか例示をおこなって確認していきましょう。

ページのトップへ


2. Variant 型の様々な用途例と VB.NET での代替


単純に考えると、Variant 型に対する VB.NET での代替は「Object型」と思いがちです。VB.NET における Object 型で定義された変数は .NET の様々なデータを扱うことができるため、 Visual Basic 6.0 に慣れた Visual Basic プログラマは、このObject 型を多用してしまう傾向にあるかもしれません。しかし、VB.NET をより有効、かつ安全に利用する場合、Variant 型の使用状況によっては、Object 型以外の利用が相応しい場合があります。ここで、改めて Variant 型の考えうる典型的な用途をいくつかあげて、VB.NET では、代替手段を使ってどのように実現されるか確認してみましょう。

Variantの代表的な用途としては、次のものが考えられます。

  1. ① 整数の集合、文字列の集合など、様々なデータの集合を扱えるオブジェクトなどを作る
  2. ② 整数、文字列など、複数種類のデータ型に1つの引数で対応できる
  3. ③ 将来必要な様々なデータの処理に対応可能にするため、あらゆるデータを受け取れる引数を定義できる

まず、①のケースのように、データの集合を管理する何らかのプログラムのパーツを実装する場合、データの集合を管理する部分を次の例のように Variant 型の要素の配列として定義すれば、コードを書き変えずして、整数の集合や文字列の集合など、様々なデータの集合として利用できるようになります。この使用方法は、Variant 型のごく自然な使用方法の1つと言えるでしょう。

例 1. VB 6.0: Variant 型の配列

Visual Basic
Dim data() As Variant
 

これに対する .NET の代替としては、Object 型要素の配列が考えられるかもしれませんが、Visual Basic 2005 では、Object 型の配列などに関して信頼性やパフォーマンスの向上につながる「ジェネリック」と呼ばれる仕組みが導入されています。(ジェネリックについては、別の機会に改めて取り上げますので、ここではこれ以上の踏み込んだ説明は省略します。)

Note: Visual Basic のジェネリックについては、次のページにも記載されています。

また、②は、具体例をあげると、インデックスを指定して何らかの結果を返すようなプロシージャを定義するとき、次の例の Item プロパティ プロシージャを呼び出す際に、インデックスとして数字 (10) を指定したり、文字列 ("Sunday") を指定したりと、異なる種類のデータ型を指定するような場合です。

例 2. VB 6.0: 異なる引数の指定

Visual Basic
Set result1 = obj.Item(10Set result2 = obj.Item("Sunday") 
 
 

このプロシージャの引数を、次のように Variant 型として定義する方法が考えられます。このプロシージャの内部では、渡された引数の型に応じて、処理を変えるなどの実装を行うことになります。

例 3. VB 6.0: Variant 型の引数

Visual Basic
Public Property Get Item(ndx As VariantAs Class1 
  
    :(省略) 
  
End Property 
 
 

ただし、この例の Variant 型の引数 ndx は、汎用的といっても、実際には、数値と文字列という、ある限られたパターンでしか使用しないことが前提になっています。この場合、VB.NET であれば代替として、次のようにプロシージャをオーバー ロード (多重定義) する方法が考えられます。

例 4. VB.NET: それぞれ、数値、文字列を引数に受け取るプロシージャ

Visual Basic
Public Overloads ReadOnly Property Item(ByVal ndx As Intger) As Class1 
    Get 
        :(省略) 
    End Get 
End Property 
  
Public Overloads ReadOnly Property Item(ByVal ndx As StringAs Class1 
    Get 
        :(省略) 
    End Get 
End Property 
 
 

このほうが、プログラムの信頼性が向上します。というのは、前述の VB 6.0 の場合では、想定外のデータ型の引数を渡たすコードを書いたとしてもコンパイル エラーにはならず、実行時まで引数が不適切であることは発覚しません。しかし、後者の VB.NET の例では、このプロシージャを呼び出すためには、引数に整数か文字列 (または、これらに暗黙的に変換可能な引数) を渡す必要があります。仮に、これらの引数の型に適合できないデータを渡すコードを書いた場合、実行時ではなく、コンパイルの時点でエラーになります。

②のこの例からも分かるように、VB.NET を有効活用すると、Variant 型が必ずしも Object 型に単純に置き換わるわけではないことが分かります。

ただし、このオーバー ロードが利用できるのは、②のケースのように、予め引数として渡すデータの型が特定のパターンに限られている場合です (ここでは、Integer 型と String 型のみ)。あらゆるデータ型を受け入れるような汎用的な引数の場合は適用できません。それが、表題にも取り上げた③のケースです。

ページのトップへ


3. 引数にあらゆるデータ型に対応できるように Variant 型にすると ...

③のケースは、引数として渡されたデータに対して何らかの計算や処理を行うプロシージャ (メソッド) を実装する際に、様々な異なるデータ型に対処できるようにするため Variant 型にするというパターンです。たとえば、注文データを引数として渡して処理を行う際に、様々な異なる注文データに対応したり、後になって注文データの仕様を変更したりできるように、引数を Variant 型にして、汎用的なメソッドにするという方法が思い当たります。しかし、よくよく考えると、引数を Variant 型にしたところで、特に汎用的なメソッドになっていないことが分かります。次の簡単な例で考えてみましょう。

例 5. VB 6.0: 様々なデータを受け取れるようにした Variant 型引数

Visual Basic
Public Sub Process(odr As Variant) 
    odr.MakeSummary 
    odr.Flag = 1 
End Sub 
 
 

この Process メソッドでは、注文データを引数 odr として受け取り、そのデータに何らかの加工をする処理を行っています。引数を Variant 型にして、様々な型のデータを受け取れるようにしてみました。しかし、一方で、Process メソッドの中を見ると、引数で受け取ったデータ (オブジェクト) に対して MakeSummary メソッドの呼び出しと、Flag プロパティへの値の設定を行っています。

つまり、引数として汎用的なデータを受け取るように定義をしてみたものの、実際は、「MakeSummary メソッドと Flag プロパティを備えた型」であること、という暗黙のコントラクト (契約) が存在するのです。このコントラクトに従わないオブジェクトが引数で渡されると、"実行時に" エラーになります。そもそも、この Process メソッドの中で、引数が特定の構造を持つことが前提になっており、Variant 型の「汎用的である」という本来の性格を失っています。

このような例に対して、VB.NET で対応する典型的な代替方法の1つは、オブジェクト指向プログラミング言語の特徴の一つである「インターフェイス」を使用する方法です。この例であれば、「MakeSummary メソッドと Flag プロパティを備えた型」というコントラクトをインターフェイスとして定義して、それを引数の型として定義するようにします。次のようになります。

例 6. VB.NET: インターフェイスの定義と、引数での利用

Visual Basic
Public Interface IOrder 
    Sub MakeSummary() 
    Property Flag() As Integer 
End Interface 
  
    : 
  
Public Sub Process(odr As IOrder) 
    odr.MakeSummary() 
    odr.Flag = 1 
End Sub 
 
 

この例では、IOrder インターフェイスとして定義して、このインターフェイスを Process メソッドの引数の型として定義していします。

このようにすることで、Process メソッドに渡すべき引数には IOrder インターフェイスというコントラクトをサポートするものであることが明示され、可読性も向上します。また、IOrder インターフェイスにある MakeSummary メソッドや Flag プロパティを定義していないオブジェクトを誤って引数に渡した場合は、"コンパイルの時点で" エラーとして検出できます。この引数に渡すべきオブジェクトは、次のように Implements ステートメントを使用して IOrder インターフェイスを実装することを宣言した上で、MakeSummary メソッドや Flag プロパティを実装しなければなりません (クラス名は任意です)。

例 7. VB.NET: インターフェイスの実装

Visual Basic
Public Class MyOrder 
    Implements IOrder 
  
    : (インターフェイスの実装) 
  
End Class 
 
 

Note: ここでは、Visual Basic が持つオブジェクト指向プログラミング言語としての特徴を生かして典型的な例を示しましたが、③のケースに対する VB.NET での代替方法は他にも考えられます。例えば、Object 型の遅延バインディング (late binding) を使用する方法もありますが、それは別の機会に取り上げます。

以上、.NET 以前の Variant 型に関して、その用途に関して改めて見つめなおし、使用するシナリオごとに、どのように VB.NET の代替方法を使用するか確認しました。状況によって、ジェネリック、オーバー ロード、また、インターフェイスというように、様々な代替手段があります。VB.NET を有効活用すると、単純に Variant 型が Object 型に置き換わるわけではないことが、確認できたかと思います。


Code Recipe .NET Framework デベロッパー センター

ページのトップへ