インターフェイスを実装するすべての型を取得する
-
09-06-2019 - |
質問
リフレクションを使用して、最小限のコードで反復を最小限に抑えながら、C# 3.0/.NET 3.5 のインターフェイスを実装するすべての型を取得するにはどうすればよいでしょうか?
もう一度書きたいのは次のとおりです。
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
解決
私のものはC# 3.0ではこれになります:)
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
基本的に、最小の反復量は常に次のようになります。
loop assemblies
loop types
see if implemented.
他のヒント
これは私にとってはうまくいきました。クラスをループして、それらが myInterface から派生したかどうかを確認します。
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
//do stuff
}
IFoo インターフェイスを実装するアセンブリ内のすべての型を検索するには、次のようにします。
var results = from type in someAssembly.GetTypes()
where typeof(IFoo).IsAssignableFrom(type)
select type;
Ryan Rinaldi の提案は間違っていたことに注意してください。0 種類が返されます。書くことはできません
where type is IFoo
type は System.Type インスタンスであり、IFoo 型になることはありません。代わりに、IFoo が型から割り当て可能かどうかを確認します。そうすれば期待通りの結果が得られます。
また、現在答えとしてマークされているアダム・ライトの提案も、同じ理由で間違っています。すべての System.Type インスタンスが IFoo 実装者ではないため、実行時に返される型は 0 であることがわかります。
これが非常に古い質問であることは承知していますが、これまでのすべての回答は何らかの形式を使用しているため、将来のユーザーのために別の回答を追加したいと思いました。 Assembly.GetTypes
.
GetTypes() は確かにすべての型を返しますが、必ずしもそれらをアクティブ化できるわけではないため、潜在的に ReflectionTypeLoadException
.
タイプをアクティブ化できない典型的な例は、返されたタイプが次の場合です。 derived
から base
しかし base
とは別のアセンブリで定義されている derived
, 、呼び出し側アセンブリが参照しないアセンブリ。
したがって、次のようになります。
Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA
入っている場合 ClassC
中にあるのは AssemblyC
次に、受け入れられた回答に従って何かを実行します。
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
それからそれは投げます ReflectionTypeLoadException
.
というのは、言及がないからです。 AssemblyA
で AssemblyC
次のことはできません。
var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);
言い換えると ClassB
ではありません ロード可能 これは、GetTypes の呼び出しによってチェックされ、スローされるものです。
したがって、ロード可能な型の結果セットを安全に修飾するには、次のようにします フィル・ハーケッド 記事 アセンブリ内のすべての型を取得する そして ジョン・スキートのコード 代わりに次のようなことを行います。
public static class TypeLoaderExtensions {
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
if (assembly == null) throw new ArgumentNullException("assembly");
try {
return assembly.GetTypes();
} catch (ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null);
}
}
}
その後:
private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
var it = typeof (IMyInterface);
return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
ここでの他の回答は使用します IsAssignableFrom
. 。も使用できます FindInterfaces
から System
名前空間、説明どおり ここ.
以下は、現在実行中のアセンブリ フォルダー内のすべてのアセンブリをチェックし、特定のインターフェイスを実装するクラスを探す例です (わかりやすくするために LINQ は避けています)。
static void Main() {
const string qualifiedInterfaceName = "Interfaces.IMyInterface";
var interfaceFilter = new TypeFilter(InterfaceFilter);
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var di = new DirectoryInfo(path);
foreach (var file in di.GetFiles("*.dll")) {
try {
var nextAssembly = Assembly.ReflectionOnlyLoadFrom(file.FullName);
foreach (var type in nextAssembly.GetTypes()) {
var myInterfaces = type.FindInterfaces(interfaceFilter, qualifiedInterfaceName);
if (myInterfaces.Length > 0) {
// This class implements the interface
}
}
} catch (BadImageFormatException) {
// Not a .net assembly - ignore
}
}
}
public static bool InterfaceFilter(Type typeObj, Object criteriaObj) {
return typeObj.ToString() == criteriaObj.ToString();
}
複数のインターフェイスを一致させる場合は、インターフェイスのリストを設定できます。
読み込まれたすべてのアセンブリをループし、そのすべての型をループして、インターフェイスが実装されているかどうかを確認します。
何かのようなもの:
Type ti = typeof(IYourInterface);
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
foreach (Type t in asm.GetTypes()) {
if (ti.IsAssignableFrom(t)) {
// here's your type in t
}
}
}
これは私にとってはうまくいきました(必要に応じて、ルックアップでシステムタイプを除外できます)。
Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
t => lookupType.IsAssignableFrom(t) && !t.IsInterface);
編集:元の質問が反復やコードの削減に関するものであったことを明確にするための編集を見たところです。演習としては問題ありませんが、現実の状況では、方法に関係なく、最速の実装が必要になります。基盤となる LINQ の外観がクールです。
これは、ロードされた型を反復処理するための Utils メソッドです。インターフェイスだけでなく通常のクラスも処理し、独自/サードパーティのコードベースで実装を探している場合は、excludeSystemTypes オプションを使用すると処理が大幅に高速化されます。
public static List<Type> GetSubclassesOf(this Type type, bool excludeSystemTypes) {
List<Type> list = new List<Type>();
IEnumerator enumerator = Thread.GetDomain().GetAssemblies().GetEnumerator();
while (enumerator.MoveNext()) {
try {
Type[] types = ((Assembly) enumerator.Current).GetTypes();
if (!excludeSystemTypes || (excludeSystemTypes && !((Assembly) enumerator.Current).FullName.StartsWith("System."))) {
IEnumerator enumerator2 = types.GetEnumerator();
while (enumerator2.MoveNext()) {
Type current = (Type) enumerator2.Current;
if (type.IsInterface) {
if (current.GetInterface(type.FullName) != null) {
list.Add(current);
}
} else if (current.IsSubclassOf(type)) {
list.Add(current);
}
}
}
} catch {
}
}
return list;
}
あまり美しくないことは認めます。
他の答えは機能しませんでした 汎用インターフェース.
これは、typeof(ISomeInterface) を typeof (T) に置き換えるだけです。
List<string> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
.Where(x => typeof(ISomeInterface).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.Select(x => x.Name).ToList();
それで、
AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
すべてのアセンブリを取得します
!x.IsInterface && !x.IsAbstract
インターフェースと抽象的なものを除外するために使用されます。
.Select(x => x.Name).ToList();
それらをリストに含めます。
やりたいことを(パフォーマンスの観点から)簡単に実行する方法はありません。
リフレクションは主にアセンブリと型を操作するため、アセンブリのすべての型を取得し、適切なインターフェイスをクエリする必要があります。以下に例を示します。
Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);
これにより、アセンブリ MyAssembly で IMyInterface を実装するすべての型が取得されます。
組み立て場所を選択する場合はさらに便利です。実装されているすべてのインターフェイスが同じ Assembly.DefinedTypes 内にあることがわかっている場合は、ほとんどのアセンブリをフィルターします。
// We get the assembly through the base class
var baseAssembly = typeof(baseClass).GetTypeInfo().Assembly;
// we filter the defined classes according to the interfaces they implement
var typeList = baseAssembly.DefinedTypes.Where(type => type.ImplementedInterfaces.Any(inter => inter == typeof(IMyInterface))).ToList();
linq コードで例外が発生したので、次の方法で実行します (複雑な拡張機能は使用しません)。
private static IList<Type> loadAllTypes(Types[] interfaces)
{
IList<Type> objects = new List<Type>();
// find all types
foreach (var interfaceType in interfaces)
foreach (var currentAsm in AppDomain.CurrentDomain.GetAssemblies())
try
{
foreach (var currentType in currentAsm.GetTypes())
if (interfaceType.IsAssignableFrom(currentType) && currentType.IsClass && !currentType.IsAbstract)
objects.Add(currentType);
}
catch { }
return objects;
}
LINQ を使用してリストを取得できます。
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
しかし本当に、そのほうが読みやすいのでしょうか?