A-TAK.COM

C#のジェネリックってすげー便利

※Amazonのアソシエイトとして、A-TAK.COMは適格販売により収入を得ています
※本サイトではその他アフィリエイトも利用しております。

シェア

img43f09a6e7dd57[1].png

NEMCHI2は通知プラグインの読み込みもできた。

やってて気づいたけど、プロトコルプラグインと通知プラグインでは実装しているインターフェースが違うだけで、特定のインターフェースを持つDLLを検索するという動きは、まるで一緒。
どうにか共通化できないかと考えてみて、ジェネリッククラスが使えそうな気がしたので、調べて試してみたら、おおーこれはかなり便利。
[2007.4.7追記]
なんかうまく行く場合となぜか、T plugin = (T)obj;のところで物言わずメソッドを抜けてしまう場合があるみたいです。エラーも出ずに。やり方がまずいのだろうか。調査中。

とりあえず、こんな感じでプラグイン検索を実装してみた。

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace NemchiCore
{
    public class PluginSearch<T> : IEnumerable<T>
    {

        private List<T> plugins = new List<T>();

        public void Search(string searchPath)
        {
            //DLLの取得
            string[] dlls = Directory.GetFiles(searchPath, “*.dll”);

            //アセンブリ取得
            foreach (string dll in dlls)
            {
                Assembly asm = Assembly.LoadFrom(dll);

                //クラス一覧を取得
                Type[] types = asm.GetTypes();
                foreach (Type dlltyp in types)
                {
                   
                    if (IncludedInterface(dlltyp, typeof(T)))
                    {
                        if (dlltyp.IsAbstract == false)
                        {
                            //インスタンス作成
                            Object obj = Activator.CreateInstance(dlltyp);
                            T plugin = (T)obj;                             plugins.Add(plugin);
                        }
                    }
                }
            }
        }

        private static bool IncludedInterface(Type dlltyp, Type includeType)
        {
            Type[] interfaces = dlltyp.GetInterfaces();
            foreach (Type interfaceTyp in interfaces)
            {
                //指定されたインターフェースを実装しているクラスを探す
                if (interfaceTyp.Name == includeType.Name)
                {
                    return true;
                }
            }

            return false;
        }

        #region IEnumerable<T> メンバ

        public IEnumerator<T> GetEnumerator()
        {
            return plugins.GetEnumerator();
        }

        #endregion

        #region IEnumerable メンバ

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return plugins.GetEnumerator();
        }

        #endregion

    }
}

IProtocolインターフェースを持つクラスのインスタンスを全部取得する場合は、

        public static List<IProtocol> GetProtocolList(string searchPath)
        {
            List<IProtocol> protocols = new List<IProtocol>();

            PluginSearch<IProtocol> searcher = new PluginSearch<IProtocol>();
            searcher.Search(searchPath);

            foreach (IProtocol protocol in searcher)
            {
                protocols.Add(protocol);               
            }

            return protocols;

        }

こんな感じ。
キャストが一カ所しかないのが素敵。
Searchの戻り値でListをそのまま返してもいいけど、将来的にListじゃなくなることも考えてForEachで回して取得するようにしてみた。
なんかちょっと変則的かな?というかPluginSearchというクラス名を変えればしっくりくるのか?
まぁ、ちょっといろいろ試してみよう。

でも、こんな感じで型に依存せずにロジックがかけるというのは便利ですね。
うまいこと使えばかなりコードがすっきりしそう。
ただ変な使い方したら一気にわかりづらくなりそうな気もしますが、そこは試行錯誤で(笑)



シェア

投稿日

カテゴリー:

投稿者:

タグ:

カテゴリ一覧