【问题标题】:Get all inherited classes of an abstract class [duplicate]获取抽象类的所有继承类[重复]
【发布时间】:2011-07-21 16:00:47
【问题描述】:

我有一个抽象类:

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 派生的新类,这样我就可以自动迭代所有实现并将例如名称添加到下拉列表中。 我只想对派生类进行编码,而不更改项目中的任何其他内容,重新编译,宾果游戏!

如果您有其他解决方案:告诉他们。

谢谢

【问题讨论】:

    标签: c# reflection inheritance


    【解决方案1】:

    假设它们都在同一个程序集中定义,您可以这样做:

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

    【讨论】:

    • 谢谢,非常有趣。需要仔细看看Activator.CreateInstance
    • 使用t.GetConstructor(Type.EmptyTypes) != null 作为附加约束,以防止尝试实例化没有无参数构造函数的类。
    【解决方案2】:

    这是一个非常常见的问题,尤其是在 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,以便对它们进行排序。
    • 您的子类必须具有相同的构造函数签名,否则会引发异常。这对我来说通常不是问题。

    【讨论】:

    • 一个类型可以同时是非抽象和非类的吗?
    • 这绝对是疯了!我不知道你可以用反射做这样的事情。
    • 如果子类可以在不同的程序集中定义呢?
    • @tobriand 这将搜索所有程序集:AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(....
    • 在过去的几年里,我已经用过几次了……有时确实会引起我的注意,尽管当有一个引用在没有一些依赖关系的情况下设法通过时。但是,是的,它抓住了子类型:)。值得注意的是,GetAssemblies() 从内存中获取 加载的 程序集,而不是 引用的 程序集。这有时会很痛苦。
    【解决方案3】:

    typeof(AbstractDataExport).Assembly 告诉您您的类型所在的程序集(假设所有类型都在同一个程序集中)。

    assembly.GetTypes() 为您提供该程序集中的所有类型,或者assembly.GetExportedTypes() 为您提供公共类型。

    遍历类型并使用type.IsAssignableFrom() 可以确定类型是否派生。

    【讨论】:

    • 感谢您的回答,assembly.gettypes() 是我需要的解决方案。
    【解决方案4】:

    这可能不是优雅的方式,但您可以迭代程序集中的所有类并调用Type.IsSubclassOf(AbstractDataExport) 每一个。

    【讨论】:

    • +1:我相信这几乎是唯一的解决方案。
    • 非常感谢!我的解决方案基于您的建议。
    猜你喜欢
    • 2014-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    相关资源
    最近更新 更多