リフレクションを使用して、最小限のコードでC#3.0 / .NET 3.5のインターフェイスを実装するすべての型を取得し、反復を最小限に抑えるにはどうすればよいですか?
これは私が書き直したいものです:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
リフレクションを使用して、最小限のコードで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.
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
に変更して、フィルターで除外します(またはp.IsClass
)。
List<string>
実装されてIEnumerable<object>
いませんが、このメソッドは.Net 4.0ではtrueを返しますが、これは共分散が実際に間違っているためです。正解はこちら
.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
これでうまくいきました。クラスをループし、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;
ライアンリナルディの提案が正しくなかったことに注意してください。0のタイプを返します。書けない
where type is IFoo
タイプは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);
言い換えれば、GetTypesへの呼び出しがチェックしてスローするものであるロード可能ではClassB
ありません。
したがって、ロード可能な型の結果セットを安全に修飾するには、このPhil Haackedの記事「アセンブリ内のすべての型とJon Skeetコード」に従って、代わりに次のようにします。
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();
}
CreateInstance
それらすべてに対してアクティブ化したかったのですが、実際のインターフェースを作成しようとしたときに例外がスローされました(このソリューションでは実際のインターフェースが邪魔だと思っていたため、しばらく混乱していました)。そこで、コードをに変更しましたGetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).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
}
}
}
編集:元の質問は反復/コードの削減に関するものであり、それは演習として十分であり、そして実際の状況では、最速の実装を望んでいることを明確にするために編集を見たところです基になるLINQがどの程度クールかを示します。
次に、読み込まれた型を反復処理するためのUtilsメソッドを示します。独自の/サードパーティのコードベースでの実装を探している場合、excludeSystemTypesオプションは、通常のクラスとインターフェイスを処理し、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;
}
それはきれいではない、私は認めます。
excludeSystemTypes
1回で2回テストするのif
ですか?
他の答えは、一般的なインターフェースでは機能しませんでした。
これは、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();
OfType Linqメソッドは、この種のシナリオに正確に使用できます。
https://docs.microsoft.com/fr-fr/dotnet/api/system.linq.enumerable.oftype?view=netframework-4.8
すでに多くの有効な回答がありますが、別の実装をType拡張として追加し、さまざまなシナリオを示す単体テストのリストを追加したいと思います。
public static class TypeExtensions
{
public static IEnumerable<Type> GetAllTypes(this Type type)
{
var typeInfo = type.GetTypeInfo();
var allTypes = GetAllImplementedTypes(type).Concat(typeInfo.ImplementedInterfaces);
return allTypes;
}
private static IEnumerable<Type> GetAllImplementedTypes(Type type)
{
yield return type;
var typeInfo = type.GetTypeInfo();
var baseType = typeInfo.BaseType;
if (baseType != null)
{
foreach (var foundType in GetAllImplementedTypes(baseType))
{
yield return foundType;
}
}
}
}
このアルゴリズムは、次のシナリオをサポートしています。
public static class GetAllTypesTests
{
public class Given_A_Sample_Standalone_Class_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(SampleStandalone);
_expectedTypes =
new List<Type>
{
typeof(SampleStandalone),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Abstract_Base_Class_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(SampleBase);
_expectedTypes =
new List<Type>
{
typeof(SampleBase),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Child_Class_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(SampleChild);
_expectedTypes =
new List<Type>
{
typeof(SampleChild),
typeof(SampleBase),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Base_Interface_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(ISampleBase);
_expectedTypes =
new List<Type>
{
typeof(ISampleBase)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Child_Interface_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(ISampleChild);
_expectedTypes =
new List<Type>
{
typeof(ISampleBase),
typeof(ISampleChild)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Implementation_Class_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(SampleImplementation);
_expectedTypes =
new List<Type>
{
typeof(SampleImplementation),
typeof(SampleChild),
typeof(SampleBase),
typeof(ISampleChild),
typeof(ISampleBase),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Interface_Instance_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
class Foo : ISampleChild { }
protected override void Given()
{
var foo = new Foo();
_sut = foo.GetType();
_expectedTypes =
new List<Type>
{
typeof(Foo),
typeof(ISampleChild),
typeof(ISampleBase),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
sealed class SampleStandalone { }
abstract class SampleBase { }
class SampleChild : SampleBase { }
interface ISampleBase { }
interface ISampleChild : ISampleBase { }
class SampleImplementation : SampleChild, ISampleChild { }
}
public IList<T> GetClassByType<T>()
{
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.ToList(p => typeof(T)
.IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
.SelectList(c => (T)Activator.CreateInstance(c));
}
linqコードで例外が発生したため、次のようにします(複雑な拡張なし)。
private static IList<Type> loadAllImplementingTypes(Type[] interfaces)
{
IList<Type> implementingTypes = 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)
implementingTypes.Add(currentType);
}
catch { }
return implementingTypes;
}
LINQを使用してリストを取得できます。
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
しかし、本当に、それはもっと読みやすいですか?