Attributes and Reflection
Part of the Object Oriented Programming section of Coddy's C# journey — lesson 44 of 70.
Attributes are metadata tags you attach to code elements like classes, methods, or properties. They don't change how code executes directly, but they provide information that can be read at runtime. Reflection is the mechanism that reads this metadata.
You apply an attribute by placing it in square brackets above the target element:
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }
[Serializable]
public class Person
{
public string Name { get; set; }
}You can create custom attributes by inheriting from Attribute:
public class AuthorAttribute : Attribute
{
public string Name { get; }
public AuthorAttribute(string name) => Name = name;
}
[Author("John")]
public class Calculator { }Reflection lets you inspect types and read attributes at runtime using the System.Reflection namespace:
Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name); // JohnYou can also discover methods, properties, and invoke them dynamically:
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
Console.WriteLine(method.Name);Attributes and reflection power many frameworks - serialization libraries use them to control JSON output, testing frameworks use them to discover test methods, and dependency injection containers use them to wire up services automatically.
Challenge
EasyLet's build a plugin discovery system that uses custom attributes and reflection to automatically find and describe classes marked as plugins. This is similar to how real frameworks discover components, test methods, or API endpoints at runtime.
You'll organize your code across three files:
PluginAttribute.cs: Create a custom attribute calledPluginAttributein thePluginSystemnamespace. Your attribute should:- Inherit from
Attribute - Have two read-only properties:
Name(string) andVersion(string) - Accept both values through its constructor
- Inherit from
Plugins.cs: Create several plugin classes in thePluginSystemnamespace. Some should be decorated with yourPluginAttribute, and one should not:ImageProcessor- marked with[Plugin("Image Processor", "1.0")]DataExporter- marked with[Plugin("Data Exporter", "2.5")]HelperUtility- NOT marked with any attribute (this is just a regular class)
Program.cs: In your main file, use reflection to discover all classes that have thePluginAttribute. For each discovered plugin, print its information. You'll need to:- Get all types from the current assembly
- Check each type for the
PluginAttribute - Extract and display the attribute's
NameandVersionproperties
Use System.Reflection to inspect types and retrieve custom attributes. The GetCustomAttribute<T>() method returns null if the attribute isn't present, so you can use this to filter which classes are actual plugins.
Print each discovered plugin in this format, one per line:
Plugin: {Name} (v{Version})The plugins should be printed in the order: ImageProcessor first, then DataExporter. The HelperUtility class should not appear in the output since it lacks the attribute.
Expected output:
Plugin: Image Processor (v1.0)
Plugin: Data Exporter (v2.5)This challenge demonstrates how frameworks use attributes as markers and reflection to discover them - the same technique powers dependency injection containers, serialization libraries, and testing frameworks!
Cheat sheet
Attributes are metadata tags placed in square brackets above code elements. They provide information that can be read at runtime:
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }
[Serializable]
public class Person
{
public string Name { get; set; }
}Create custom attributes by inheriting from Attribute:
public class AuthorAttribute : Attribute
{
public string Name { get; }
public AuthorAttribute(string name) => Name = name;
}
[Author("John")]
public class Calculator { }Reflection lets you inspect types and read attributes at runtime using the System.Reflection namespace:
Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name); // JohnDiscover methods and properties dynamically:
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
Console.WriteLine(method.Name);The GetCustomAttribute<T>() method returns null if the attribute isn't present, allowing you to filter types based on attribute presence.
Try it yourself
using System;
using System.Reflection;
using PluginSystem;
class Program
{
public static void Main(string[] args)
{
// TODO: Get all types from the current assembly
// Hint: Use Assembly.GetExecutingAssembly().GetTypes()
// TODO: Loop through each type and check if it has the PluginAttribute
// Hint: Use type.GetCustomAttribute<PluginAttribute>()
// TODO: If the attribute exists, print the plugin info in the format:
// Plugin: {Name} (v{Version})
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator8Advanced OOP Concepts
Composition over InheritanceGenerics (Classes & Methods)Delegates and EventsAttributes and ReflectionIDisposable & using StatementDependency Injection Basics