私の製品を使用する開発者が、インターフェイスを実装し、アセンブリを実行フォルダーにドロップするだけで製品を拡張できるようにしたいと考えています。これらのタイプを見つける方法は?
4 に答える
マイクロソフトのソリューションを使用しないのはなぜですか?
私が理解している限り、それはあなたが探しているものを正確に解決します
結局、私はビンで見つけたすべてのdllをロードして検索することを選択しました。これがほとんどのコードです。キー関数は、私が探しているジェネリックインターフェイスを見つける「IsAssignableToGenericType」関数です。
これは、リフレクションを介したインターフェイスの実装のほとんどのソリューションを提供したリンクであると私は信じてい ます
static AssemblyLocator()
{
AllDlls = GetAllDlls();
SubscribersInBin = GetSubscribersInBin();
}
public static IEnumerable<Type> TypesImplementingInterface(Assembly[] assemblies, Type desiredType)
{
return assemblies
.SelectMany(assembly => assembly.GetTypes())
.Where(type => IsAssignableToGenericType(type, desiredType));
}
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
if (givenType == null) throw new ArgumentNullException("givenType");
if (genericType == null) throw new ArgumentNullException("genericType");
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
{
if (it.IsGenericType)
{
if (it.GetGenericArguments()[0].Name.Equals(genericType.GetGenericArguments()[0].Name))
return true;
}
}
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return (baseType.IsGenericType &&
baseType.GetGenericTypeDefinition() == genericType) ||
IsAssignableToGenericType(baseType, genericType);
}
private static ReadOnlyCollection<string> GetAllDlls()
{
string binFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
IList<string> dllFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList();
return new ReadOnlyCollection<string>(dllFiles);
}
private static ReadOnlyCollection<Type> GetSubscribersInBin()
{
IList<Assembly> assembliesFoundInBin = new List<Assembly>();
foreach (var item in AllDlls)
{
var assembly = System.Reflection.Assembly.LoadFrom(item);
assembliesFoundInBin.Add(assembly);
}
var typesInBin = TypesImplementingInterface(assembliesFoundInBin.ToArray(), typeof(ISubscriber<T>));
return new ReadOnlyCollection<Type>(typesInBin.ToList<Type>());
}
新しいアセンブリを別のフォルダー (呼び出し、プラグイン、または拡張機能など) に配置し、実行時にインスタンス化することができます。または、製品アセンブリのリストを維持し、Path.GetFiles を使用して、リストに表示されるアセンブリをすべて削除します。
これをユーザー定義ツールに使用します。
private static void getImplementedTypes(Type baseType, Assembly assembly, IList<Type> list) {
Type[] types = assembly.GetExportedTypes();
foreach (Type t in types) {
if (baseType.IsInterface) {
Type[] interfaces = t.GetInterfaces();
foreach (Type i in interfaces) {
if (i == baseType) list.Add(t);
}
}
else {
if ((!list.Contains(t)) && (t.IsSubclassOf(baseType)) && (!t.IsAbtract)) {
list.Add(t);
}
}
}
return n;
}
ループでは、ツール ディレクトリの Directory.GetFiles によって見つかったすべての DLL (または EXE) を調べます。
Assembly assembly = Assembly.LoadFile("toolbox.dll");
List<Type> types = new List<Type>();
getImplementedTypes(typeof(ToolBase), assembly, types);
ToolBase theTool = Activator.CreateInstance(type, true) as ToolBase;
これは、基本クラスだけでなくインターフェイスでも機能します。
実装されたクラスを別の方法で見つける方法がわかりません。これには時間がかかる場合があります。したがって、検索する DLL がわかっている場合は、それらをループするだけです。