Custom attributes in C# can be used to add metadata or additional information to our code elements, such as classes, methods, and properties. It allows us to add runtime and user-defined properties to our code annotations that can be inspected and used. Custom attributes are a form of declarative programming, providing a means to describe additional information about code elements without directly affecting the code’s behavior.
Imagine you’re a shipping company manager and have to assign certain packages with specific handling instructions. To do this, create a set of unique labels or tags that indicate the different package handling requirements. Let’s look at the steps on how we can apply the above example:
Custom attributes: We create custom labels or tags such as “Fragile,” “Perishable,” and “Do Not Stack.”
Application: These custom attributes are attached to specific packages based on their contents.
Instructions: Handlers can quickly identify packages marked with these custom attributes and apply appropriate care or handling procedures. For instance, a package labeled “Fragile” might need to be handled delicately, while “Perishable” packages might require expedited shipping.
Let’s have a look at the key points about custom attributes:
Declaration: Custom attributes are defined by creating custom attribute classes. These classes inherit from the System.Attribute
class and can have properties, fields, or other members to store additional information.
Application: We can apply custom attributes to code elements like classes, methods, properties, fields, and parameters. It is typically done by including the attribute’s name in square brackets before the code element.
Metadata: Custom attributes add metadata to code, which tools, libraries, or other code at runtime can use. For example, attributes can be used for documentation generation, serialization, and validation.
Reflection: We can use reflection to access the information stored in custom attributes at runtime. Reflection allows us to inspect and query attributes applied to code elements.
Attribute usage: The attribute usage property can be used when defining a custom attribute to determine where it can be applied and whether it can be applied to the same code element at multiple instances.
Define the custom attribute class: Creates a reusable attribute to attach additional information to classes, methods, and properties. Line 1 Inherits from the Attribute
class, making it a custom attribute. Line 3 has a property SomeProperty
to store custom data.
public class OurCustomAttribute : Attribute{public string SomeProperty { get; set; }}
Apply our custom attribute: Our custom attribute can be applied to many code elements, including properties, methods, and classes. On line 1 the attribute is applied using square brackets []
before the element’s declaration. On lines 4 and 14 the SomeProperty
is set with different values for each element.
[OurCustomAttribute(SomeProperty = "ClassAttribute")]public class OurClass{[OurCustomAttribute(SomeProperty = "MethodAttribute")]public void YourMethod(){Console.WriteLine("Executing YourMethod");int result = AddNumbers(7, 3);Console.WriteLine($"Result of adding numbers: {result}");}[OurCustomAttribute(SomeProperty = "OurPropertyAttribute")]public string OurProperty { get; set; }}
Access custom attribute values: We can use reflection to access the values of our custom attribute at runtime. On lines 3, 15, and 27 the GetCustomAttribute
method is used to retrieve the attribute instances, and the values of SomeProperty
are then printed. If the attribute is not found for a particular code element, a corresponding message is printed:
Type type = typeof(OurClass);OurCustomAttribute classAttribute = (OurCustomAttribute)type.GetCustomAttribute(typeof(OurCustomAttribute));if (classAttribute != null){string propertyValue = classAttribute.SomeProperty;Console.WriteLine($"Class Attribute: {propertyValue}");}else{Console.WriteLine("Class Attribute not found.");}MethodInfo methodInfo = type.GetMethod("YourMethod");OurCustomAttribute methodAttribute = (OurCustomAttribute)methodInfo.GetCustomAttribute(typeof(OurCustomAttribute));if (methodAttribute != null){string methodValue = methodAttribute.SomeProperty;Console.WriteLine($"Method Attribute: {methodValue}");}else{Console.WriteLine("Method Attribute not found.");}PropertyInfo propertyInfo = type.GetProperty("OurProperty");OurCustomAttribute propertyAttribute = (OurCustomAttribute)propertyInfo.GetCustomAttribute(typeof(OurCustomAttribute));if (propertyAttribute != null){string propertyValue = propertyAttribute.SomeProperty;Console.WriteLine($"Property Attribute: {propertyValue}");}else{Console.WriteLine("Property Attribute not found.");}
Let’s take a look at the code example on how we can use custom attributes to annotate our code elements with additional metadata and then use reflection to access and utilize that metadata at runtime.
using System;using System.Reflection;public class OurCustomAttribute : Attribute{public string SomeProperty { get; set; }}[OurCustomAttribute(SomeProperty = "ClassAttribute")]public class OurClass{[OurCustomAttribute(SomeProperty = "MethodAttribute")]public void YourMethod(){Console.WriteLine("Executing YourMethod");// Some code inside YourMethodint result = AddNumbers(7, 3);Console.WriteLine($"Result of adding numbers: {result}");}[OurCustomAttribute(SomeProperty = "OurPropertyAttribute")]public string OurProperty { get; set; }private int AddNumbers(int a, int b){return a + b;}}class Program{static void Main(){Type type = typeof(OurClass);OurCustomAttribute classAttribute = (OurCustomAttribute)type.GetCustomAttribute(typeof(OurCustomAttribute));if (classAttribute != null){string propertyValue = classAttribute.SomeProperty;Console.WriteLine($"Class Attribute: {propertyValue}");}else{Console.WriteLine("Class Attribute not found.");}MethodInfo methodInfo = type.GetMethod("YourMethod");OurCustomAttribute methodAttribute = (OurCustomAttribute)methodInfo.GetCustomAttribute(typeof(OurCustomAttribute));if (methodAttribute != null){string methodValue = methodAttribute.SomeProperty;Console.WriteLine($"Method Attribute: {methodValue}");}else{Console.WriteLine("Method Attribute not found.");}PropertyInfo propertyInfo = type.GetProperty("OurProperty");OurCustomAttribute propertyAttribute = (OurCustomAttribute)propertyInfo.GetCustomAttribute(typeof(OurCustomAttribute));if (propertyAttribute != null){string propertyValue = propertyAttribute.SomeProperty;Console.WriteLine($"Property Attribute: {propertyValue}");}else{Console.WriteLine("Property Attribute not found.");}}}
Lines 4–7: A custom attribute class OurCustomAttribute
is defined, which inherits from Attribute
. This custom attribute has a property SomeProperty
that can be set with a string value.
Lines 9–29: The OurCustomAttribute
is applied to different code elements:
It’s applied to the OurClass
class, with SomeProperty
set to “ClassAttribute.”
It’s applied to the YourMethod
method, with SomeProperty
set to “MethodAttribute.”
It’s applied to the OurProperty
property, with SomeProperty
set to “OurPropertyAttribute.”
Lines 31–72: In the Main
method, the code uses reflection to retrieve and print the custom attribute values applied to the class, method, and property:
It obtains the type of the OurClass
class.
It attempts to get the OurCustomAttribute
applied to the class, method, and property using GetCustomAttribute
.
If an attribute is found for a specific code element, it retrieves and prints the SomeProperty
value. If an attribute is not found, it displays an appropriate message.
Free Resources