Получить все унаследованные классы абстрактного класса

У меня есть абстрактный класс:

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 - ваш друг здесь. Прежде чем внедрять это, вы можете рассмотреть аспекты производительности этого: "Отражение всегда дорого": -)