連載
» 2005年09月30日 05時00分 公開

.NET TIPS:メソッドやプロパティの有無を確認して呼び出すには?

[一色政彦,デジタルアドバンテージ]
.NET TIPS
Insider.NET


「.NET TIPS」のインデックス

連載目次

 「TIPS:サイド・バイ・サイドによりCLRバージョンを指定するには?」のようにして.NET Framework 1.1で作成したプログラムを.NET Framework 1.0のCLR上で動かす場合や、.NET Framework 1.1と1.0の2つの開発環境で同じソース・コードを共有するような場合、.NET Framework 1.1で導入された(つまり.NET Framework 1.0では利用できない)メソッドやパラメータについては、その存在の有無(=利用可能かどうか)をいったん確認したうえで、<動的>に(メソッド名やプロパティ名で)呼び出した方がよい。これは直接的なメソッドやプロパティの呼び出しを避けることで、.NET Frameworkの各バージョン間の差異をソース・コードで吸収できるようになるからだ(具体的には本稿のサンプル・プログラムの実行結果を参照)。

 そこで本稿ではメソッドやプロパティが利用可能かどうかを確認する方法と、それらを動的に(プロパティ名やメソッド名を指定して)呼び出す方法を紹介する。

メソッドの存在確認とメソッド名によるメソッドの呼び出し

 まずはメソッドの有無を確認する方法だ。これには、メソッドが所属するクラスの、Typeオブジェクトを取得し(TypeオブジェクトはC#ならtypeof演算子で、Visual Basic .NETならGetType式で取得できる)、メソッド名をパラメータに指定してTypeオブジェクトのGetMethodメソッドを呼び出せばよい。

 GetMethodメソッドの戻り値は、MethodInfoクラス(System.Reflection名前空間)のオブジェクトである。戻り値として正しくMethodInfoオブジェクトが返されればそのメソッドは存在し、null(C#の場合。VB.NETではNothing)が戻されればそのメソッドは存在しない。

 次に、存在が確認できたメソッドをメソッド名を指定して呼び出す。これには、MethodInfoオブジェクトのInvokeメソッドを呼び出せばよい。

 Invokeメソッドの第1パラメータには(メソッドが含まれるクラスの)インスタンスを設定し(静的メソッドの場合は「null」を指定すればよい)、第2パラメータにはそのメソッドのパラメータのリストをobject配列として渡せばよい(パラメータがない場合は「null」でよい)。

 以上がメソッドにおける処理だ。次にプロパティの処理を行っていこう。

プロパティの存在確認とプロパティ名によるプロパティへのアクセス

 まずプロパティの有無を確認するには、先ほどと同じように所属するクラスのTypeオブジェクトを取得して、プロパティ名をパラメータに指定してTypeオブジェクトのGetPropertyメソッドを呼び出せばよい。

 GetPropertyメソッドの戻り値は、PropertyInfoクラス(System.Reflection名前空間)のオブジェクトである。戻り値として正しくPropertyInfoオブジェクトが返されればそのプロパティは存在し、null(C#の場合。VB.NETではNothing)が戻されればそのメソッドは存在しない。

 次にプロパティに値を設定する。これには、PropertyInfoオブジェクトのSetValueメソッド(=プロパティのセッター)を呼び出せばよい。

 SetValueメソッドの第1パラメータには(プロパティが含まれるクラスの)インスタンスを設定し、第2パラメータにはプロパティに設定する値を指定する。、第3パラメータには、そのプロパティが「インデックス付きプロパティ」(=インデクサ)の場合に、そのインデックス値を指定する(インデックス付きプロパティではない場合は「null」や「Nothing」を指定する)。

 逆にプロパティ値を取得する場合には、GetValueメソッド(=プロパティのゲッター)を呼び出せばよい。

 GetValueメソッドの第1パラメータには(プロパティが含まれるクラスの)インスタンスを設定し、第2パラメータはそのプロパティが「インデックス付きプロパティ」(=インデクサ)の場合、そのインデックス値を指定する(インデックス付きプロパティではない場合には「null」や「Nothing」を指定する)。

 以上の手順で、実際にメソッドとプロパティの有無をチェックしているのが、次のサンプル・プログラムである。このサンプル・プログラムでは、まずFileクラス(System.IO名前空間)の静的メソッドであるGetCreationTimeUtcメソッドを使ってファイル作成時のUTC(Coordinated Universal Time:協定世界時刻)時間を取得してそれをコンソールに出力し、次にFileInfoクラス(System.IO名前空間)のCreationTimeUtcプロパティを使って同じくファイル作成時のUTC時間を設定(プロパティのセッター)したり、取得(プロパティのゲッター)したりして、再度コンソールに出力している。

 これらのGetCreationTimeUtcメソッドやCreationTimeUtcプロパティは、.NET Framework 1.1で追加されたもので、.NET Framework 1.0環境には存在しない。

using System;
using System.IO;
using System.Reflection;

namespace ConsoleApplication1
{
  class Class1
  {
    [STAThread]
    static void Main(string[] args)
    {
      string path = @"C:\WINDOWS\シャボン.bmp";
      DateTime ct = DateTime.UtcNow;
      DateTime newct = DateTime.UtcNow;

      // メソッドの有無を確認する
      Type typeFile = typeof(File);
      MethodInfo mi =
        typeFile.GetMethod("GetCreationTimeUtc");
      if (mi != null)
      {
        object[] methodParams = { path };
        // メソッドの呼び出し
        // 以下は「ct = File.GetCreationTimeUtc(path);」と同じ意味
        ct = (DateTime)mi.Invoke(null, methodParams);
        Console.WriteLine(ct);
      }
      else
      {
        Console.WriteLine("メソッドがありません。");
      }

      // 日時情報を5日進める(テスト用)
      ct = ct.AddDays(5);

      // プロパティの有無を確認する
      Type typeFileInfo = typeof(FileInfo);
      PropertyInfo pi =
        typeFileInfo.GetProperty("CreationTimeUtc");
      if (pi != null)
      {
        FileInfo fi = new FileInfo(path);
        // プロパティのセッターの呼び出し
        // 以下は「fi.CreationTimeUtc = ct;」と同じ意味
        pi.SetValue(fi, ct, null);
        // プロパティのゲッターの呼び出し
        // 以下は「newct = fi.CreationTimeUtc;」と同じ意味
        newct = (DateTime)pi.GetValue(fi, null);
        Console.WriteLine(newct);
      }
      else
      {
        Console.WriteLine("プロパティがありません。");
      }

      Console.Read();
    }
  }
}

Imports System.IO
Imports System.Reflection


Module Module1

  Sub Main()
    Dim path As String = "C:\WINDOWS\シャボン.bmp"
    Dim ct As DateTime = DateTime.UtcNow
    Dim newct As DateTime = DateTime.UtcNow

    ' メソッドの有無を確認する
    Dim typeFile As Type = GetType(File)
    Dim mi As MethodInfo = _
      typeFile.GetMethod("GetCreationTimeUtc")
    If Not mi Is Nothing Then
      Dim methodParams() As Object = {path}

      ' メソッドの呼び出し
      ' 以下は「ct = File.GetCreationTimeUtc(path)」と同じ意味
      ct = CType(mi.Invoke(Nothing, methodParams), DateTime)
      Console.WriteLine(ct)
    Else
      Console.WriteLine("メソッドがありません。")
    End If

    ' 日時情報を5日進める(テスト用)
    ct = ct.AddDays(5)

    ' プロパティの有無を確認する
    Dim typeFileInfo As Type = GetType(FileInfo)
    Dim pi As PropertyInfo = _
      typeFileInfo.GetProperty("CreationTimeUtc")
    If Not pi Is Nothing Then
      Dim fi As FileInfo = New FileInfo(path)
      ' プロパティのセッターの呼び出し
      ' 以下は「fi.CreationTimeUtc = ct」と同じ意味
      pi.SetValue(fi, ct, Nothing)
      ' プロパティのゲッターの呼び出し
      ' 以下は「newct = fi.CreationTimeUtc」と同じ意味
      newct = CType(pi.GetValue(fi, Nothing), DateTime)
      Console.WriteLine(newct)
    Else
      Console.WriteLine("プロパティがありません。")
    End If

    Console.Read()

  End Sub

End Module

メソッドやプロパティの有無を確認して呼び出すサンプル・プログラム(上:C#、下:VB.NET)

 これを.NET Framework 1.1と1.0のそれぞれの環境で実行すると、次のような結果になる。

2004 07 01 10:38:20
2004 07 06 10:38:20

.NET Framework 1.1環境でのサンプル・プログラムの実行結果

メソッドがありません。
プロパティがありません。

.NET Framework 1.0環境でのサンプル・プログラムの実行結果

 .NET Framework 1.1と1.0の両環境でソース・コードを共有する場合には、本稿のサンプル・プログラムのように、メソッドやプロパティの有無をいったん確認してから、それらを(InvokeメソッドやSetValue/GetValueメソッドで)動的に呼び出すように実装すれば、コンパイル時にも実行時にもエラーにならない(メソッドやプロパティを動的に呼び出さないと、そのメソッドやプロパティが存在しない環境では当然エラーになる)。

カテゴリ:クラス・ライブラリ 処理対象:リフレクション
使用ライブラリ:Typeクラス(System名前空間)
使用ライブラリ:MethodInfoクラス(System.Reflection名前空間)
使用ライブラリ:PropertyInfoクラス(System.Reflection名前空間)
使用ライブラリ:Fileクラス(System.IO名前空間)
使用ライブラリ:FileInfoクラス(System.IO名前空間)
関連TIPS:サイド・バイ・サイドによりCLRバージョンを指定するには?


「.NET TIPS」のインデックス

.NET TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。