基于 MSDN示例

假设我们在独立桌面应用程序中有一些带有HelpAttribute的C#类。是否可以枚举具有此类属性的所有类?以这种方式识别课程是否有意义?自定义属性将用于列出可能的菜单选项,选择项将带到此类的屏幕实例。课程/项目的数量会慢慢增长,但我认为这样我们就可以避免在其他地方列举它们。

有帮助吗?

解决方案

是的,绝对的。使用反思:

static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
    foreach(Type type in assembly.GetTypes()) {
        if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
            yield return type;
        }
    }
}

其他提示

好吧,您必须枚举加载到当前应用程序域的所有程序集中的所有类。为此,您可以调用 GetAssemblies AppDomain <上的>方法 / a>当前应用域的实例。

从那里,您可以致电 GetExportedTypes (如果您只想要公共类型)或 <上的“noreferrer”> GetTypes 代码>程序集 以获取程序集中包含的类型。

然后,您将调用 GetCustomAttributes Type 方法 a>实例,传递您想要查找的属性的类型。

您可以使用LINQ为您简化:

var typesWithMyAttribute =
    from a in AppDomain.CurrentDomain.GetAssemblies()
    from t in a.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

上面的查询将为您提供应用了您的属性的每种类型,以及分配给它的属性的实例。

请注意,如果您的应用程序域中加载了大量程序集,那么该操作可能会很昂贵。您可以使用并行LINQ 来减少操作时间,如此:

var typesWithMyAttribute =
    // Note the AsParallel here, this will parallelize everything after.
    from a in AppDomain.CurrentDomain.GetAssemblies().AsParallel()
    from t in a.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

在特定的 Assembly 上过滤它很简单:

Assembly assembly = ...;

var typesWithMyAttribute =
    from t in assembly.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

如果程序集中包含大量类型,那么您可以再次使用Parallel LINQ:

Assembly assembly = ...;

var typesWithMyAttribute =
    // Partition on the type list initially.
    from t in assembly.GetTypes().AsParallel()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

其他答案参考 GetCustomAttributes 。添加此示例作为使用 IsDefined 的示例

Assembly assembly = ...
var typesWithHelpAttribute = 
        from type in assembly.GetTypes()
        where type.IsDefined(typeof(HelpAttribute), false)
        select type;

如前所述,反思是要走的路。如果你打算频繁地调用它,我强烈建议缓存结果,因为反射,特别是每个类的枚举,可能会很慢。

这是我的代码片段,它贯穿所有已加载程序集中的所有类型:

// this is making the assumption that all assemblies we need are already loaded.
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) 
{
    foreach (Type type in assembly.GetTypes())
    {
        var attribs = type.GetCustomAttributes(typeof(MyCustomAttribute), false);
        if (attribs != null && attribs.Length > 0)
        {
            // add to a cache.
        }
    }
}

这是在已接受的解决方案之上的性能增强。迭代虽然所有类都很慢,因为有这么多。有时您可以过滤掉整个装配体而不查看任何类型。

例如,如果您要查找自己声明的属性,则不希望任何系统DLL包含具有该属性的任何类型。 Assembly.GlobalAssemblyCache属性是检查系统DLL的快速方法。当我在一个真实的程序上尝试这个时,我发现我可以跳过30,101种类型而且我只需要检查1,983种类型。

过滤的另一种方法是使用Assembly.ReferencedAssemblies。假设您希望具有特定属性的类,并且该属性是在特定程序集中定义的,那么您只关心该程序集和引用它的其他程序集。在我的测试中,这比检查GlobalAssemblyCache属性更有帮助。

我将这两者结合起来并且速度更快。以下代码包括两个过滤器。

        string definedIn = typeof(XmlDecoderAttribute).Assembly.GetName().Name;
        foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            // Note that we have to call GetName().Name.  Just GetName() will not work.  The following
            // if statement never ran when I tried to compare the results of GetName().
            if ((!assembly.GlobalAssemblyCache) && ((assembly.GetName().Name == definedIn) || assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn)))
                foreach (Type type in assembly.GetTypes())
                    if (type.GetCustomAttributes(typeof(XmlDecoderAttribute), true).Length > 0)

如果是 Portable .NET限制,以下代码应该有效:

    public static IEnumerable<TypeInfo> GetAtributedTypes( Assembly[] assemblies, 
                                                           Type attributeType )
    {
        var typesAttributed =
            from assembly in assemblies
            from type in assembly.DefinedTypes
            where type.IsDefined(attributeType, false)
            select type;
        return typesAttributed;
    }

或者使用基于循环状态的 yield return

的大量程序集
    public static IEnumerable<TypeInfo> GetAtributedTypes( Assembly[] assemblies, 
                                                           Type attributeType )
    {
        foreach (var assembly in assemblies)
        {
            foreach (var typeInfo in assembly.DefinedTypes)
            {
                if (typeInfo.IsDefined(attributeType, false))
                {
                    yield return typeInfo;
                }
            }
        }
    }

我们可以改进安德鲁的答案并将整个事物转换为一个LINQ查询。

    public static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly)
    {
        return assembly.GetTypes().Where(type => type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0);
    }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top