问题基于MSDN示例.
假设我们在独立桌面应用程序中有一些带有HelpAttribute的C#类.是否可以枚举具有此类属性的所有类?以这种方式识别课程是否有意义?自定义属性将用于列出可能的菜单选项,选择项将带到此类的屏幕实例.课程/项目的数量将缓慢增长,但我认为这样我们可以避免在其他地方列举所有课程/项目.
是的,一点没错.使用反射:
static IEnumerableGetTypesWithHelpAttribute(Assembly assembly) { foreach(Type type in assembly.GetTypes()) { if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) { yield return type; } } }
那么,您必须枚举加载到当前应用程序域中的所有程序集中的所有类.要做到这一点,你会调用GetAssemblies
方法的AppDomain
当前应用程序域实例.
从那里,您将调用GetExportedTypes
(如果您只需要公共类型)或GetTypes
每个调用Assembly
以获取程序集中包含的类型.
然后,您将在每个实例上调用该GetCustomAttributes
方法Type
,并传递您要查找的属性的类型.
您可以使用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() };
上面的查询将为您提供应用了您的属性的每种类型,以及分配给它的属性的实例.
请注意,如果您的应用程序域中加载了大量程序集,则该操作可能会很昂贵.您可以使用Parallel 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() };
过滤特定的内容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() };
如果程序集中包含大量类型,那么您可以再次使用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() };
其他答案参考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)