114 votes

Obtenez toutes les classes héritées d'une classe abstraite

Résolu! Solution sur le fond.

Bonjour!

J'ai une classe abstraite:

abstract class AbstractDataExport
{
        public string name;
        public abstract bool ExportData();
}

J'ai des cours qui sont dérivés de 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()
    {
        ...
    }
}

Est-il possible de faire quelque chose comme cela? (Pseudo-code:)

foreach (Implementation imp in Reflection.GetInheritedClasses(AbstractDataExport)
{
    AbstractDataExport derivedClass = Implementation.CallConstructor();
    Console.WriteLine(derivedClass.name)
}

avec une sortie comme

CsvExporter
XmlExporter

?

L'idée derrière cela est de simplement créer une nouvelle classe qui est dérivée à partir de AbstractDataExport donc je peut itérer sur toutes les implémentations automatiquement et ajouter par exemple les noms d'une liste Déroulante Liste. Je veux juste le code de la classe dérivée sans rien changer d'autre dans le projet, recompiler, bingo!

Si vous avez d'autres solutions: tell em.

Merci

Solution:

Type[] types = Assembly.GetExecutingAssembly().GetTypes();
foreach (Type type in types)
{
    if (type.IsSubclassOf(typeof(AbstractDataExport)))
    {
        //Find all Constructors with no arguments (Type.EmptyTypes) and call them - with no arguments (null)
        AbstractDataExport abcde = (AbstractDataExport)type.GetConstructor(Type.EmptyTypes).Invoke(null);
        abcde.ExportData();
    }
}

167voto

Repo Man Points 1655

C'est un problème commun, en particulier dans les applications avec interface graphique, que je suis surpris, il n'y a pas un BCL classe pour ce faire hors de la boîte. Voici comment je le fais.

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;
    }
}

Quelques remarques:

  • Ne vous inquiétez pas le "coût" de cette opération - vous n'allez faire qu'une seule fois (je l'espère), et même alors, il est pas aussi lentement que vous le pensez.
  • Vous devez utiliser Assembly.GetAssembly(typeof(T)) parce que votre classe de base peut être dans une autre assemblée.
  • Vous devez utiliser les critères de l' type.IsClass et !type.IsAbstract parce qu'il va lever une exception si vous essayez d'instancier une interface ou une classe abstraite.
  • J'ai comme le fait de forcer les classes énumérées à mettre en oeuvre IComparable , de sorte qu'ils peuvent être triées.
  • Votre enfant des classes doivent avoir le même constructeur signatures, sinon ça va lever une exception. Généralement, cela n'est pas un problème pour moi.

59voto

Lee Points 63849

En supposant qu'ils sont tous définis dans le même assemblage que vous pouvez faire:

IEnumerable<AbstractDataExport> exporters = typeof(AbstractDataExport).Assembly
    .GetTypes()
    .Where(t => t.IsSubclassOf(typeof(AbstractDataExport) && !t.IsAbstract))
    .Select(t => (AbstractDataExport)Activator.CreateInstance(t));

11voto

WorldIsRound Points 1259

Il peut ne pas être le moyen élégant, mais vous pouvez parcourir toutes les classes à l'assemblée et à invoquer Type.IsSubclassOf(AbstractDataExport) pour chacun d'eux.

3voto

typeof(AbstractDataExport).Assembly vous dit un assemblage de vos types sont situés dans (en supposant que tous sont dans le même).

assembly.GetTypes() vous donne tous les types de l'assemblée ou de l' assembly.GetExportedTypes() vous donne les types de publics.

Itération sur les types et l'utilisation de type.IsAssignableFrom() vous donne de savoir si le type est dérivé.

0voto

James Poulose Points 350

Ainsi, la Réflexion est votre ami ici. Avant la mise en œuvre, vous pourriez envisager les aspects de la performance de que - "la Réflexion est toujours coûteux" :-)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X