How Enumerate All Classes with Custom Class Attribute

How enumerate all classes with custom class attribute?

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;
}
}
}

Find all classes with a specific attribut


public static IEnumerable<Type> GetTypesWithMyAttribute(Assembly assembly)
{
foreach(Type type in assembly.GetTypes())
{
if (Attribute.IsDefined(type, typeof(MyAttribute)))
yield return type;
}
}

Or:

public static List<Type> GetTypesWithMyAttribute(Assembly assembly)
{
List<Type> types = new List<Type>();

foreach(Type type in assembly.GetTypes())
{
if (type.GetCustomAttributes(typeof(MyAttribute), true).Length > 0)
types.Add(type);
}

return types;
}

Linq VS my method benchmark (100000 iterations):

Round 1
My Approach: 2088ms
Linq Approach 1: 7469ms
Linq Approach 2: 2514ms

Round 2
My Approach: 2055ms
Linq Approach 1: 7082ms
Linq Approach 2: 2149ms

Round 3
My Approach: 2058ms
Linq Approach 1: 7001ms
Linq Approach 2: 2249ms

Benchmark code:

[STAThread]
public static void Main()
{
List<Type> list;

Stopwatch watch = Stopwatch.StartNew();

for (Int32 i = 0; i < 100000; ++i)
list = GetTypesWithMyAttribute(Assembly.GetExecutingAssembly());

watch.Stop();

Console.WriteLine("ForEach: " + watch.ElapsedMilliseconds);

watch.Restart();

for (Int32 i = 0; i < 100000; ++i)
list = GetTypesWithMyAttributeLinq1(Assembly.GetExecutingAssembly());

Console.WriteLine("Linq 1: " + watch.ElapsedMilliseconds);

watch.Restart();

for (Int32 i = 0; i < 100000; ++i)
list = GetTypesWithMyAttributeLinq2(Assembly.GetExecutingAssembly());

Console.WriteLine("Linq 2: " + watch.ElapsedMilliseconds);

Console.Read();
}

public static List<Type> GetTypesWithMyAttribute(Assembly assembly)
{
List<Type> types = new List<Type>();

foreach (Type type in assembly.GetTypes())
{
if (Attribute.IsDefined(type, typeof(MyAttribute)))
types.Add(type);
}

return types;
}

public static List<Type> GetTypesWithMyAttributeLinq1(Assembly assembly)
{
return assembly.GetTypes()
.Where(t => t.GetCustomAttributes().Any(a => a is MyAttribute))
.ToList();
}

public static List<Type> GetTypesWithMyAttributeLinq2(Assembly assembly)
{
return assembly.GetTypes()
.Where(t => Attribute.IsDefined(t, typeof(MyAttribute)))
.ToList();
}

C# Get List of All Classes with Specific Attribute Parameter

You can just add this to your query:

where type.GetCustomAttribute<GenericConfigAttribute>().MyEnum == GenericConfigType.Type1

For example:

var type1Types =
from type in Assembly.GetExecutingAssembly().GetTypes()
where type.IsDefined(typeof(GenericConfigAttribute), false)
where type.GetCustomAttribute<GenericConfigAttribute>().MyEnum
== GenericConfigType.Type1
select type;

Or simplified slightly (and safer):

var type1Types =
from type in Assembly.GetExecutingAssembly().GetTypes()
where type.GetCustomAttribute<GenericConfigAttribute>()?.MyEnum
== GenericConfigType.Type1
select type;

And if you need to cope with multiple attributes on the same type, you can do this:

var type1Types =
from type in Assembly.GetExecutingAssembly().GetTypes()
where type.GetCustomAttributes<GenericConfigAttribute>()
.Any(a => a.MyEnum == GenericConfigType.Type1)
select type;

Scan all classes for a given custom attribute

at first you have to create TRttiContext, then get all loaded classes using getTypes. after that you can filter types by TypeKind = tkClass;
next step is to enumerate attributes and check if it has your attribute;

attribute and test-class delcaration:

unit Unit3;

interface
type
TMyAttribute = class(TCustomAttribute)
end;

[TMyAttribute]
TTest = class(TObject)

end;

implementation

initialization
TTest.Create().Free(); //if class is not actually used it will not be compiled

end.

and then find it:

program Project3;
{$APPTYPE CONSOLE}

uses
SysUtils, rtti, typinfo, unit3;

type TMyAttribute = class(TCustomAttribute)

end;

var ctx : TRttiContext;
t : TRttiType;
attr : TCustomAttribute;
begin
ctx := TRttiContext.Create();

try
for t in ctx.GetTypes() do begin
if t.TypeKind <> tkClass then continue;

for attr in t.GetAttributes() do begin
if attr is TMyAttribute then begin
writeln(t.QualifiedName);
break;
end;
end;
end;
finally
ctx.Free();
readln;
end;
end.

output is Unit3.TTest

Call RegisterClass to register a class with the streaming system.... Once classes are registered, they can be loaded or saved by the component streaming system.

so if you don't need component streaming (just find classes with some attribute), there is no need to RegisterClass

Best practices to scan all classes and methods for custom attribute

Reflection is very slow...

I think you've go the basics there. I'd recommend you change your code slightly to avoid the extra full scan taking place.

If you have to do this more than once, I'd also recommend you consider caching the results for whatever period of time is appropriate.

Sorta like this pseudo-code:

... (optional caches) ...
IDictionary<Type, IEnumerable<Attributes>> typeAttributeCache = new ...
IDictionary<MethodInfo, IEnumerable<Attributes>> methodAttributeCache = new ...

... (in another method or class) ...
foreach assembly in GetAssemblies()
foreach type in assembly.GetTypes()
typeAttributes = typeAttributeCache.TryGet(...) // you know the correct syntax, trying to be brief

if (typeAttributes is null)
typeAttributes = type.GetCustomAttributes().OfType<TypeImLookingFor>();
typeAttributeCache[type] = typeAttributes;

foreach methodInfo in type.GetMethods()
methodAttributes = methodAttributeCache.TryGet(...) // same as above

if (methodAttributes is null)
methodAttributes = methodInfo.GetCustomAttributes().OfType<TypeImLookingFor>();
methodAttributeCache[type] = methodAttributes;

// do what you need to do

get all types in assembly with custom attribute

I wouldn't think you can dodge enumerating every type in the assembly, checking for the attribute, but you could use LINQ to make the query easier to understand:

Assembly assembly = ...
var types = from type in assembly.GetTypes()
where Attribute.IsDefined(type, typeof(FindableAttribute))
select type;

EDIT: Moved from MemberInfo.GetCustomAttributes to Attribute.IsDefined based on Marc Gravell's suggestion.

Get the class name using class attributes

You can get the types that have the attribute using reflection like this:

using System;
using System.Reflection;

public class MarkerAttribute : Attribute {
}

[Marker]
class Program {
static void Main(string[] args) {
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) {
foreach (Attribute attribute in type.GetCustomAttributes()) {
MarkerAttribute markerAttribute = attribute as MarkerAttribute;
if (markerAttribute != null) {
Console.WriteLine(type.FullName);
}
}
}
}
}

Find all classes with an attribute containing a specific property value

Replace Assembly.GetExecutingAssembly() with typeof(AbstractParentClass).Assembly.



Related Topics



Leave a reply



Submit