How to Get all classes with a custom attribute in C#

By FoxLearn 3/12/2025 3:31:34 AM   15
To find all classes with a custom attribute, the first step is to gather all types within the assembly and then use the IsDefined method to filter the types that are marked with the custom attribute.
using System.Reflection;

var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsDefined(typeof(ServiceAttribute)));

This code searches for classes in the current assembly that have the [Service] attribute.

[Service]
public class EmailService : IEmailService
{
    public void SendEmail(string to, string subject, string body)
    {
        // Send email logic here
    }
}

This can be useful in scenarios like auto-discovery of service classes during application startup or for implementing dependency injection.

In this article, we'll also cover searching across all loaded assemblies, handling inherited custom attributes, and retrieving the values of custom attributes.

Search Across All Loaded Assemblies

To search through all loaded assemblies, use AppDomain.CurrentDomain.GetAssemblies() to retrieve all the currently loaded assemblies. Then, loop through each assembly and use GetType() and IsDefined() to find classes with the specified custom attribute.

var types = from assembly in AppDomain.CurrentDomain.GetAssemblies()
            from type in assembly.GetTypes()
            where type.IsDefined(typeof(ServiceAttribute))
            select type;

Alternatively, using method syntax, you could do:

AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(a => a.GetTypes().Where(t => t.IsDefined(typeof(ServiceAttribute))));

If you want to search in assemblies that haven’t been loaded yet, you can opt for the MetadataReader approach.

Handle Inherited Custom Attributes

Custom attributes can be inherited.

[Service]
public abstract class ServiceBase
{
    public abstract void ExecuteService();
}

public class EmailService : ServiceBase
{
    public override void ExecuteService()
    {
        // Implement service execution logic here
    }
}

The [Service] attribute is defined on the ServiceBase class, and is inherited by EmailService. Searching for classes with the [Service] attribute would return both ServiceBase and EmailService by default. However, you might want to filter this to only get the derived classes, not the base class.

Filter Out Abstract Classes

You typically wouldn’t want abstract classes to appear in the search results. You can filter them out by checking the Type.IsAbstract property:

assembly.GetTypes().Where(t => t.IsDefined(typeof(ServiceAttribute)) && !t.IsAbstract)

This would return only EmailService, excluding the ServiceBase abstract class.

Filter Out Classes That Inherited the Custom Attribute

By default, IsDefined(customAttributeType) will return true even if the attribute is inherited. If you want to avoid returning classes that inherit the attribute but don’t define it themselves, use the inherit parameter:

assembly.GetTypes().Where(t => t.IsDefined(typeof(ServiceAttribute), inherit: false))

In this case, if EmailService only inherited the [Service] attribute from ServiceBase, it would be excluded from the results.

Getting Custom Attribute Values

When a custom attribute contains values, you may want to extract those values.

For example, consider this Service attribute with a value for the service type:

[Service("Email")]
public class EmailService : IEmailService
{
    // Email service logic
}

To retrieve the custom attribute values, use GetCustomAttribute<T>() to get the attribute object:

var assembly = Assembly.GetExecutingAssembly();

foreach (var type in assembly.GetTypes())
{
    var serviceAttribute = type.GetCustomAttribute<ServiceAttribute>();

    if (serviceAttribute != null)
    {
        Console.WriteLine($"Service={type.Name} Type={serviceAttribute.ServiceType}");
    }
}

If the custom attribute isn’t defined on the type, GetCustomAttribute will return null. Otherwise, it returns the custom attribute object, and you can access its properties. For the above code, this would output:

Service=EmailService Type=Email

This allows you to work with the values specified in the custom attribute, providing more detailed information for your application logic.