如何枚举具有自定义类属性的所有类?
基于MSDN示例的问题 。
假设我们在独立的桌面应用程序中有一些带有HelpAttribute的C#类。 枚举所有具有这种属性的类是否可能? 这样识别class级有意义吗? 自定义属性将用于列出可能的菜单选项,select项目将带来这样的类的实例。 类/项目的数量将会增长缓慢,但是这样我们可以避免在其他地方列举它们。
是的,一点没错。 使用reflection:
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) { foreach(Type type in assembly.GetTypes()) { if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) { yield return type; } } }
那么,你将不得不枚举所有加载到当前应用程序域的程序集中的所有类。 要做到这一点,你可以在当前应用程序域的AppDomain
实例上调用GetAssemblies
方法 。
从那里,你可以在每个Assembly
上调用GetExportedTypes
(如果你只需要publictypes的话)或GetExportedTypes
来获取程序集中包含的types。
然后,您可以在每个Type
实例上调用GetCustomAttributes
方法 ,传递您希望查找的属性的types。
你可以使用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>() };
上面的查询将为您提供每种types的属性,以及分配给它的属性的实例。
请注意,如果您有大量程序集加载到您的应用程序域中,则该操作可能会很昂贵。 您可以使用Parallel LINQ来减less操作的时间,如下所示:
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>() };
如果程序集中有大量的types,那么你可以再次使用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;
如前所述,反思是要走的路。 如果你打算经常打电话,我强烈build议caching结果,因为反思,尤其是通过每个class级列举,可能会很慢。
这是我的所有加载的程序集中的所有types的代码片段:
// 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. } } }
这是可接受的解决scheme之上的性能增强。 迭代,虽然所有的类可以是缓慢的,因为有这么多。 有时你可以在不查看任何types的情况下过滤出整个程序集。
例如,如果您正在查找自己声明的属性,则不期望任何系统DLL包含具有该属性的任何types。 Assembly.GlobalAssemblyCache属性是检查系统DLL的快速方法。 当我在一个真正的程序上尝试这个时,我发现我可以跳过30,101个types,而我只需要检查1,983个types。
另一种过滤方法是使用Assembly.ReferencedAssemblies。 假设你想要一个具有特定属性的类,并且该属性是在特定的程序集中定义的,那么你只关心该程序集和引用它的其他程序集。 在我的testing中,这比检查GlobalAssemblyCache属性略有帮助。
我把这两者结合起来,让它更快。 下面的代码包含两个filter。
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)
在可移植.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; } } } }