C# 如何枚举具有自定义类属性的所有类?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/607178/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How enumerate all classes with custom class attribute?
提问by tomash
Question based on MSDN example.
基于MSDN 示例的问题。
Let's say we have some C# classes with HelpAttribute in standalone desktop application. Is it possible to enumerate all classes with such attribute? Does it make sense to recognize classes this way? Custom attribute would be used to list possible menu options, selecting item will bring to screen instance of such class. Number of classes/items will grow slowly, but this way we can avoid enumerating them all elsewhere, I think.
假设我们在独立桌面应用程序中有一些带有 HelpAttribute 的 C# 类。是否可以枚举具有此类属性的所有类?以这种方式识别类有意义吗?自定义属性将用于列出可能的菜单选项,选择项目将显示此类的屏幕实例。类/项目的数量将缓慢增长,但我认为这样我们可以避免在其他地方枚举它们。
采纳答案by Andrew Arnott
Yes, absolutely. Using Reflection:
是的,一点没错。使用反射:
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) {
if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
yield return type;
}
}
}
回答by casperOne
Well, you would have to enumerate through all the classes in all the assemblies that are loaded into the current app domain. To do that, you would call the GetAssemblies
methodon the AppDomain
instance for the current app domain.
好吧,您必须枚举加载到当前应用程序域中的所有程序集中的所有类。为此,您将在当前应用程序域的实例上调用该GetAssemblies
方法AppDomain
。
From there, you would call GetExportedTypes
(if you only want public types) or GetTypes
on each Assembly
to get the types that are contained in the assembly.
从那里,您将调用GetExportedTypes
(如果您只需要公共类型)或调用GetTypes
每个Assembly
来获取程序集中包含的类型。
Then, you would call the GetCustomAttributes
extension methodon each Type
instance, passing the type of the attribute you wish to find.
然后,您将在每个实例上调用GetCustomAttributes
扩展方法Type
,传递您希望查找的属性的类型。
You can use LINQ to simplify this for you:
您可以使用 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>() };
The above query will get you each type with your attribute applied to it, along with the instance of the attribute(s) assigned to it.
上面的查询将为您提供应用了您的属性的每种类型,以及分配给它的属性的实例。
Note that if you have a large number of assemblies loaded into your application domain, that operation could be expensive. You can use Parallel LINQto reduce the time of the operation, like so:
请注意,如果将大量程序集加载到应用程序域中,则该操作可能会很昂贵。您可以使用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>() };
Filtering it on a specific Assembly
is simple:
在特定条件下过滤它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>() };
And if the assembly has a large number of types in it, then you can use Parallel LINQ again:
如果程序集中有大量类型,那么您可以再次使用 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>() };
回答by CodingWithSpike
As already stated, reflection is the way to go. If you are going to call this frequently, I highly suggest caching the results, as reflection, especially enumerating through every class, can be quite slow.
如前所述,反思是要走的路。如果你要经常调用它,我强烈建议缓存结果,因为反射,尤其是枚举每个类,可能会很慢。
This is a snippet of my code that runs through all the types in all loaded assemblies:
这是我的代码片段,它贯穿所有加载的程序集中的所有类型:
// 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.
}
}
}
回答by Jay Walker
Other answers reference GetCustomAttributes. Adding this one as an example of using IsDefined
其他答案参考GetCustomAttributes。添加此作为使用IsDefined的示例
Assembly assembly = ...
var typesWithHelpAttribute =
from type in assembly.GetTypes()
where type.IsDefined(typeof(HelpAttribute), false)
select type;
回答by Lorenz Lo Sauer
In case of the Portable .NET limitations, the following code should work:
在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;
}
or for a large number of assemblies using loop-state based yield return
:
或者对于使用基于循环状态的大量程序集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;
}
}
}
}
回答by Trade-Ideas Philip
This is a performance enhancement on top of the accepted solution. Iterating though all classes can be slow because there are so many. Sometimes you can filter out an entire assembly without looking at any of its types.
这是在公认的解决方案之上的性能增强。迭代所有类可能会很慢,因为有很多。有时您可以过滤掉整个程序集而无需查看其任何类型。
For example if you are looking for an attribute that you declared yourself, you don't expect any of the system DLLs to contain any types with that attribute. The Assembly.GlobalAssemblyCache property is a quick way to check for system DLLs. When I tried this on a real program I found I could skip 30,101 types and I only have to check 1,983 types.
例如,如果您正在寻找您自己声明的属性,您不希望任何系统 DLL 包含具有该属性的任何类型。Assembly.GlobalAssemblyCache 属性是检查系统 DLL 的快速方法。当我在一个真实的程序上尝试这个时,我发现我可以跳过 30,101 种类型,而我只需要检查 1,983 种类型。
Another way to filter is to use Assembly.ReferencedAssemblies. Presumably if you want classes with a specific attribute, and that attribute is defined in a specific assembly, then you only care about that assembly and other assemblies that reference it. In my tests this helped slightly more than checking the GlobalAssemblyCache property.
另一种过滤方法是使用 Assembly.ReferencedAssemblies。大概如果您想要具有特定属性的类,并且该属性是在特定程序集中定义的,那么您只关心该程序集和引用它的其他程序集。在我的测试中,这比检查 GlobalAssemblyCache 属性稍有帮助。
I combined both of these and got it even faster. The code below includes both filters.
我将这两者结合起来,并得到了更快的结果。下面的代码包括两个过滤器。
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)
回答by Tachyon
We can improve on Andrew's answer and convert the whole thing into one LINQ query.
我们可以改进 Andrew 的回答并将整个内容转换为一个 LINQ 查询。
public static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly)
{
return assembly.GetTypes().Where(type => type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0);
}