Answers:
是的,一点没错。使用反射:
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) {
if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
yield return type;
}
}
}
Select
扩展方法,并且编译器将生成状态机,就像您Select
由于使用调用时一样yield return
。最后,任何的性能提升可能在大多数情况下获得通过micro-优化。
好吧,您将必须枚举加载到当前应用程序域中的所有程序集中的所有类。为此,您可以在当前应用程序域的实例上调用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<HelpAttribute>() };
上面的查询将为您提供应用了属性的每种类型,以及分配给它的属性的实例。
请注意,如果您在应用程序域中加载了大量程序集,则该操作可能会很昂贵。您可以使用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<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)
在便携式.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;
}
}
}
}