Uzyskanie wszystkich typów implementujących interfejs
Używając reflection, jak Mogę uzyskać wszystkie typy, które implementują interfejs z C# 3.0/. NET 3.5 z najmniejszym kodem i minimalizując iteracje?
Oto co chcę napisać od nowa:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
12 answers
Mój byłby taki w c # 3.0:)
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
W zasadzie najmniejszą liczbą iteracji zawsze będzie:
loop assemblies
loop types
see if implemented.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-06 18:41:47
Aby znaleźć wszystkie typy w zestawie, które implementują interfejs IFoo:
var results = from type in someAssembly.GetTypes()
where typeof(IFoo).IsAssignableFrom(type)
select type;
Zauważ, że sugestia Ryana Rinaldiego była błędna. Zwróci 0 typów. Nie możesz napisać
where type is IFoo
Ponieważ typ jest systemem.Type instance, i nigdy nie będzie typu IFoo. Zamiast tego sprawdzasz, czy IFoo można przypisać z tego typu. To przyniesie oczekiwane rezultaty.
Również sugestia Adama Wrighta, która jest obecnie oznaczona jako odpowiedź, jest niepoprawna, a dla tego samego powód. W czasie wykonywania, zobaczysz 0 typów wrócić, ponieważ wszystkie System.Instancje typu nie były implementatorami IFoo.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 20:44:18
To mi pomogło. Zapętla klasy i sprawdza, czy są one derrived z myInterface
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
//do stuff
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-11 15:06:02
Doceniam, że to bardzo stare pytanie, ale pomyślałem, że dodam kolejną odpowiedź dla przyszłych użytkowników, ponieważ wszystkie odpowiedzi do tej pory używają jakiejś formy Assembly.GetTypes
.
Podczas gdy GetTypes() rzeczywiście zwróci wszystkie typy, nie musi to oznaczać, że możesz je aktywować i potencjalnie rzucić ReflectionTypeLoadException
.
Klasycznym przykładem na to, że nie można uaktywnić typu jest, gdy typ zwracany jest derived
z base
, Ale base
jest zdefiniowany w innym assembly od derived
, assembly, do którego nie odwołuje się wywołujący assembly.
Więc powiedzmy, że mamy:
Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA
Jeśli w ClassC
które jest w {[12] } wtedy robimy coś zgodnie z przyjętą odpowiedzią:
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
Wtedy rzuci ReflectionTypeLoadException
.
To dlatego, że bez odniesienia do AssemblyA
W AssemblyC
nie byłbyś w stanie:
var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);
Innymi słowy ClassB
nie jest ładowane co jest czymś, co wywołanie GetTypes sprawdza i rzuca.
Więc aby bezpiecznie zakwalifikować zestaw wyników dla typów ładowanych, to zgodnie z tym Phil Haacked artykuł Pobierz wszystkie typy W Zestawie i Jon Skeet code zamiast tego zrobisz coś w stylu:
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);
}
}
}
A potem:
private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
var it = typeof (IMyInterface);
return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:59
Inne odpowiedzi tutaj użyć IsAssignableFrom
. Możesz również użyć FindInterfaces
z przestrzeni nazw System
, jak opisano tutaj.
Oto przykład, który sprawdza wszystkie złożenia w aktualnie wykonującym folderze assembly, szukając klas implementujących określony interfejs(unikając LINQ dla przejrzystości).
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();
}
Możesz skonfigurować listę interfejsów, jeśli chcesz dopasować więcej niż jeden.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-07 12:19:49
Pętla przez wszystkie załadowane zespoły, Pętla przez wszystkie ich typy i sprawdzić, czy zaimplementowali interfejs.
Coś w stylu:
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
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-28 12:40:06
To zadziałało dla mnie (jeśli chcesz, możesz wykluczyć typy systemów w wyszukiwarce):
Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
t => lookupType.IsAssignableFrom(t) && !t.IsInterface);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-29 09:55:20
Edit: właśnie widziałem edycję, aby wyjaśnić, że pierwotne pytanie dotyczyło redukcji iteracji / kodu i to wszystko dobrze i dobrze jako ćwiczenie, ale w rzeczywistych sytuacjach będziesz chciał najszybszej implementacji, niezależnie od tego, jak fajnie wygląda LINQ.
Oto moja metoda Utils do iteracji przez załadowane typy. Obsługuje zarówno zwykłe klasy, jak i interfejsy, a opcja excludeSystemTypes znacznie przyspiesza działanie, jeśli szukasz implementacje we własnej / zewnętrznej bazie kodowej.
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;
}
Przyznam, że nie jest ładna.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-28 12:40:53
Nie ma łatwego sposobu (jeśli chodzi o wydajność), aby zrobić to, co chcesz zrobić.
Reflection działa głównie z assembly i typami, więc będziesz musiał pobrać wszystkie typy assembly i odpytywać je o odpowiedni interfejs. Oto przykład:
Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);
Dzięki temu uzyskasz wszystkie typy implementujące interfejs IMyInterface w złożeniu MyAssembly
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 20:13:17
Mam wyjątki w kodzie linq więc robię to w ten sposób (bez skomplikowanego rozszerzenia):
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-27 10:47:08
Inne odpowiedzi nie działały z ogólnym interfejsem .
Ten robi, wystarczy zastąpić typeof (ISomeInterface) przez 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();
Więc z
AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
Otrzymujemy wszystkie zespoły
!x.IsInterface && !x.IsAbstract
Jest używany do wykluczenia interfejsu i abstrakcyjnych oraz
.Select(x => x.Name).ToList();
Mieć je na liście.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-20 08:27:36
Przydałby się jakiś LINQ żeby dostać listę:
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
Ale naprawdę, czy to jest bardziej czytelne?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 20:18:59