web-dev-qa-db-ja.com

抽象クラスのすべての継承クラスを取得します

私には抽象クラスがあります:

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から派生した新しいクラスを作成するだけであるため、すべての実装を自動的に反復処理し、たとえばドロップダウンリストに名前を追加できます。私は、プロジェクトの他の何も変更せずに、派生クラスをコーディングし、再コンパイル、ビンゴしたいだけです!

代替ソリューションがある場合:emに伝えます。

ありがとう

104
trampi

これは、特にGUIアプリケーションでは非常に一般的な問題であるため、これをすぐに実行できる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を強制的に実装して、ソートできるようにします。
  • 子クラスには、同じコンストラクターシグネチャが必要です。そうでない場合、例外がスローされます。通常、これは私にとって問題ではありません。
155
Repo Man

それらがすべて同じアセンブリで定義されていると仮定すると、次のことができます。

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

それはエレガントな方法ではないかもしれませんが、アセンブリ内のすべてのクラスを繰り返して、それぞれに対してType.IsSubclassOf(AbstractDataExport)を呼び出すことができます。

11
WorldIsRound

typeof(AbstractDataExport).Assemblyは、タイプが配置されているアセンブリを示します(すべてが同じであると仮定)。

Assembly.GetTypes()はそのアセンブリのすべての型を提供し、_Assembly.GetExportedTypes()はパブリックな型を提供します。

タイプを繰り返し処理し、type.IsAssignableFrom()を使用すると、タイプが派生しているかどうかがわかります。

3