Получить все унаследованные классы абстрактного класса
У меня есть абстрактный класс:
abstract class AbstractDataExport
{
public string name;
public abstract bool ExportData();
}
У меня есть классы, которые получены из AbstractDataExport:
class XmlExport : AbstractDataExport
{
new public string name = "XmlExporter";
public override bool ExportData()
{
...
}
}
class CsvExport : AbstractDataExport
{
new public string name = "CsvExporter";
public override bool ExportData()
{
...
}
}
Можно ли сделать что-то подобное? (Псевдокод:)
foreach (Implementation imp in Reflection.GetInheritedClasses(AbstractDataExport)
{
AbstractDataExport derivedClass = Implementation.CallConstructor();
Console.WriteLine(derivedClass.name)
}
с выходом вроде
CsvExporter
XmlExporter
?
Идея заключается в том, чтобы просто создать новый класс, который получен из AbstractDataExport, поэтому я могу автоматически выполнять все реализации и добавлять, например, имена в раскрывающийся список.
Я просто хочу закодировать производный класс без изменения чего-либо еще в проекте, перекомпилировать, бинго!
Если у вас есть альтернативные решения: tell em.
Спасибо
Ответы
Ответ 1
Это такая распространенная проблема, особенно в приложениях с графическим интерфейсом, что я удивлен, что для этого не существует класса BCL. Вот как я это делаю.
public static class ReflectiveEnumerator
{
static ReflectiveEnumerator() { }
public static IEnumerable<T> GetEnumerableOfType<T>(params object[] constructorArgs) where T : class, IComparable<T>
{
List<T> objects = new List<T>();
foreach (Type type in
Assembly.GetAssembly(typeof(T)).GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(T))))
{
objects.Add((T)Activator.CreateInstance(type, constructorArgs));
}
objects.Sort();
return objects;
}
}
Несколько примечаний:
- Не беспокойтесь о "стоимости" этой операции - вы будете делать это только один раз (надеюсь), и даже тогда это не так медленно, как вы думаете.
- Вам нужно использовать
Assembly.GetAssembly(typeof(T))
, потому что ваш базовый класс может быть в другой сборке.
- Вам нужно использовать критерии
type.IsClass
и !type.IsAbstract
, потому что это вызовет исключение, если вы попытаетесь создать экземпляр интерфейса или абстрактного класса.
- Мне нравится заставлять перечисленные классы реализовать
IComparable
, чтобы их можно было сортировать.
- В ваших дочерних классах должны быть одинаковые сигнатуры конструктора, иначе это вызовет исключение. Обычно это не проблема для меня.
Ответ 2
Предполагая, что все они определены в одной сборке, вы можете сделать:
IEnumerable<AbstractDataExport> exporters = typeof(AbstractDataExport)
.Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(AbstractDataExport)) && !t.IsAbstract)
.Select(t => (AbstractDataExport)Activator.CreateInstance(t));
Ответ 3
Это может быть не изящный способ, но вы можете перебирать все классы в сборке и вызывать Type.IsSubclassOf(AbstractDataExport)
для каждого из них.
Ответ 4
typeof(AbstractDataExport).Assembly
указывает на сборку, в которой расположены ваши типы (при условии, что все они одинаковы).
assembly.GetTypes()
предоставляет вам все типы в этой сборке или assembly.GetExportedTypes()
предоставляет типы, которые являются общедоступными.
Итерация по типам и использование type.IsAssignableFrom()
дает вам тип вывода.
Ответ 5
Хорошо, Reflection - ваш друг здесь. Прежде чем внедрять это, вы можете рассмотреть аспекты производительности этого: "Отражение всегда дорого": -)