tom*_*ash 144 c# attributes class enumerate custom-attributes
问题基于MSDN示例.
假设我们在独立桌面应用程序中有一些带有HelpAttribute的C#类.是否可以枚举具有此类属性的所有类?以这种方式识别课程是否有意义?自定义属性将用于列出可能的菜单选项,选择项将带到此类的屏幕实例.课程/项目的数量将缓慢增长,但我认为这样我们可以避免在其他地方列举所有课程/项目.
And*_*ott 190
是的,一点没错.使用反射:
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) {
if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
yield return type;
}
}
}
Run Code Online (Sandbox Code Playgroud)
cas*_*One 99
那么,您必须枚举加载到当前应用程序域中的所有程序集中的所有类.要做到这一点,你会调用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>() };
Run Code Online (Sandbox Code Playgroud)
上面的查询将为您提供应用了您的属性的每种类型,以及分配给它的属性的实例.
请注意,如果您的应用程序域中加载了大量程序集,则该操作可能会很昂贵.您可以使用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>() };
Run Code Online (Sandbox Code Playgroud)
过滤特定的内容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>() };
Run Code Online (Sandbox Code Playgroud)
如果程序集中包含大量类型,那么您可以再次使用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>() };
Run Code Online (Sandbox Code Playgroud)
Jay*_*ker 32
其他答案参考GetCustomAttributes.添加这个作为使用IsDefined的示例
Assembly assembly = ...
var typesWithHelpAttribute =
from type in assembly.GetTypes()
where type.IsDefined(typeof(HelpAttribute), false)
select type;
Run Code Online (Sandbox Code Playgroud)
Cod*_*ike 10
如前所述,反思是要走的路.如果你打算频繁调用它,我强烈建议缓存结果,因为反射,尤其是每个类的枚举,可能会很慢.
这是我的代码片段,它贯穿所有已加载程序集中的所有类型:
// 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.
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是在已接受的解决方案之上的性能增强.迭代虽然所有类都很慢,因为有这么多.有时您可以过滤掉整个装配而不查看任何类型.
例如,如果您要查找自己声明的属性,则不希望任何系统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)
Run Code Online (Sandbox Code Playgroud)