レイトバインディングで特定のインターフェイスを実装したクラスのオブジェクトを作り呼び出す方法

リフレクションを用いて実行時にあるアセンブリに含まれる型をチェックできます。

あるインターフェイスを実装したタイプでは、インターフェイスを定義したアセンブリもロード可能な位置に配置しておく必要があります。

アセンブリのファイル名を指定してアセンブリをロードして、その中のタイプが特定のインターフェイスを実装しているかどうかチェックする方法を紹介します。

IMyInterface1 インターフェイスの定義

IMyInterface1.cs で次のように IMyInterface1 を定義

namespace MyApp.Interfaces
{
  public interface IMyInterface1
  {
    void SayHello();
  }
}

あとで GAC にインストールするので、サインしておきます。

アセンブリのサイン

IMyInterface1 を実装するクラス

IMyInterface1 を実装するクラスを次の Class1 のようにします。 Class2, Class3 は同様のメソッドを持ってはいますが、IMyInterface1 のインプリメントという形にはなっていません。

using System;

namespace MyClass1
{
  public class Class1 : MyApp.Interfaces.IMyInterface1
  {
    public void Foo()
    {
      Console.WriteLine("Foo() in Class1");
    }

    public void SayHello()
    {
      Console.WriteLine("SayHello() method in Class1 was called.");
    }
  }

  public class Class2
  {
    public void Foo()
    {
      Console.WriteLine("Foo() in Class2");
    }

    public void SayHello()
    {
      Console.WriteLine("SayHello() method in Class2 was called.");
    }
  }

  public class Class3
  {
    public void Foo()
    {
      Console.WriteLine("Foo() in Class3");
    }

    public void SayHello()
    {
      Console.WriteLine("SayHello() method in Class3 was called.");
    }
  }
}

なお、上記インターフェイスの定義とクラスの定義は別々のアセンブリで行っています。 Visual Studio でいえば別々のプロジェクトとして作っています。

アセンブリのサイン

このためインターフェイス定義は MyApp.Interfaces.dll で行われ、クラス定義は MyClass1.dll で行われます。

GAC へのインストール

次にインターフェイスの定義アセンブリを GAC に登録します。これによって同じシステム内のどこからでもこのアセンブリをロード可能になります。

> gacutil /i MyApp.Interfaces.dll
Microsoft (R) .NET Global Assembly Cache Utility.  
Version 4.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

逆にこれをしないと、MyClass1.dll の Class1 は IMyInterface1 を実装しますが、実行時に MyApp.Interfaces.dll が見つからないために失敗します。

実行時にアセンブリをロードしてインターフェイスの有無をチェック

それでは上記のアセンブリを読み込んで、その中のタイプで IMyInterface1 インターフェイスを実装しているものがあるかどうか実行時にチェックできるか試してみましょう。

テスト用のコードは次のとおりです。 C:\keisuke.oyama\src\test\bin\class には MyClass1.dll を置いてます。

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

namespace LateBindTestApp1
{
  class Program
  {
    static void Main(string[] args)
    {
      DirectoryInfo di = new DirectoryInfo(
        @"C:\keisuke.oyama\src\test\bin\class\");

      foreach (FileInfo fi in di.GetFiles())
      {
        Assembly a = Assembly.LoadFile(fi.FullName);

        foreach (Type t in a.GetTypes())
        {
          Console.Write(t.FullName);

          Type i = t.GetInterface(
            "MyApp.Interfaces.IMyInterface1", 
            true);

          if (i == null)
          {
            Console.WriteLine(" - NO IMyInterface1");
          }
          else
          {
            Console.WriteLine(" - Has IMyInterface1");
            ConstructorInfo ci = t.GetConstructor(Type.EmptyTypes);
            MyApp.Interfaces.IMyInterface1 myint1
              = ci.Invoke(null) as MyApp.Interfaces.IMyInterface1;
            myint1.SayHello();
          }
        }
      }

    }
  }
}

この実行結果は次のとおりです。

実行時の型のチェック

なお、前述のとおり GAC に MyApp.Interfaces.dll を登録しておかないと、次のように System.IO.FileNotFoundException が発生します。

Unhandled Exception: System.IO.FileNotFoundException: 
Could not load file or assembly 'MyApp.Interfaces, 
Version=1.0.0.0, Culture=neutral, PublicKeyToken=f38a28
22f7d9569a' or one of its dependencies. The system 
cannot find the file specified.

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説