RunningCSharp

.net中心の開発話。記事は個人の見解であり、所属組織を代表するものてはありません。

C#ユーザーがVBの遅延バインディングに驚いた話など

色々あって、Visual Studio 2008でVBをやっています。

触ってみた結果、多数のスタティックメソッドや「改行にアンダーバー必須」といった文法関連など色々なC#との使い勝手の違いを感じましたが、一番違いに驚いたのは下記コードの動作です。

    Dim testobj As Object
    'オブジェクト型の変数に日付型を代入
    testobj = DateTime.Now
    'Objectクラスが持っていない「Month」プロパティを、遅延バインディングで使える
    Console.WriteLine(testobj.Month & "月")

思い起こせば、VB6の頃は当たり前のように遅延バインディングを使ったコードを書いていましたが、今見るとtestobjにMonthプロパティがないクラスのインスタンスを入れると実行時エラーが出せてしまう事から、少々おっかない印象を受けてしまいます。

なお上記のコードをC#で動くように書くと、下記のようになります。

    object testobj;
    testobj = DateTime.Now;
    //TypeクラスのGetPropertyメソッドでプロパティ情報を取得し、オブジェクトのプロパティ値取得を行う
    Console.WriteLine(testobj.GetType().GetProperty("Month").GetValue(testobj).ToString() + "月");

リフレクションを用いてプロパティ情報を取得した後、プロパティの実際の値を取得する流れになります。

少々冗長な気もしますが、リフレクションを使うと下記のように、変数testobjのプロパティの有無をチェックする事が容易です。

    object testobj;
    testobj = DateTime.Now;
    //Monthプロパティを取得
    System.Reflection.PropertyInfo prop = testobj.GetType().GetProperty("Month");
    if (prop != null)
    {//testobjに入ったオブジェクトに、Monthプロパティが存在する場合にのみ処理が実行される
        Console.WriteLine(prop.GetValue(testobj).ToString() + "月");
    }

プロパティの有無をチェックすることで、実行時エラーを防げます。

もちろんVBでも、リフレクションを用いてプロパティの有無をチェックできます。

    Dim testobj As Object
    testobj = DateTime.Now
    'リフレクションでMonthプロパティの情報を取得
    Dim prop As System.Reflection.PropertyInfo = testobj.GetType().GetProperty("Month")
    If (prop <> Nothing) Then
        'testobjにMonthプロパティが存在する場合のみ、処理が実行される
        Console.WriteLine(testobj.Month & "月")
    End If

コードは長くなりますが、遅延バインディングの対象となるオブジェクト型の変数に何が入ってくるかわからない時にはぜひこのチェックは行ったほうが良さそうです。

遅延バインディングを止めて、きちんと型チェックをしてから実行したい場合は下記のような感じで。

Option Strict OnDim testobj As Object
    testobj = DateTime.Now
    'リフレクションでMonthプロパティの情報を取得
    Dim prop As System.Reflection.PropertyInfo = testobj.GetType().GetProperty("Month")
    If (prop <> Nothing) Then
        'testobjにMonthプロパティが存在する場合のみ、処理が実行される
        Console.WriteLine(prop.GetValue(testobj).ToString() & "月")
    End If

なお、C#4.0からはdynamic型が使えるため、C#でも遅延バインディング的な書き方が可能です。

    dynamic testobj;
    testobj = DateTime.Now;
    Console.WriteLine(testobj.Month + "月");

VBの遅延バインディングと同じ理由で、testobjの内容に変更が多い場合はリフレクションのほうがお勧めです。