C# > Advanced C# > Attributes and Reflection > Custom Attributes
Custom Attribute for Validating String Length
This example demonstrates how to create a custom attribute to validate the length of a string property. We'll define an attribute called StringLengthValidationAttribute
and apply it to a property in a class. Then, we'll use reflection to check if the attribute is present and perform the validation.
Defining the Custom Attribute
First, we define the StringLengthValidationAttribute
class, inheriting from System.Attribute
. AttributeUsage
specifies where this attribute can be applied (AttributeTargets.Property
), whether multiple instances are allowed (AllowMultiple = false
), and whether it's inherited (Inherited = true
). The constructor takes the maximum allowed length as a parameter and stores it in a private field. The IsValid
method checks if the provided string is valid based on the configured maximum length. The MaxLength property exposes the max length value.
using System;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class StringLengthValidationAttribute : Attribute
{
private readonly int _maxLength;
public StringLengthValidationAttribute(int maxLength)
{
_maxLength = maxLength;
}
public bool IsValid(string value)
{
if (string.IsNullOrEmpty(value))
{
return true; // Consider null/empty strings valid
}
return value.Length <= _maxLength;
}
public int MaxLength => _maxLength;
}
Applying the Attribute to a Class Property
Here, we apply the StringLengthValidationAttribute
to the Username
and Email
properties of the User
class. The Username
property can have a maximum length of 50 characters, and the Email
property can have a maximum length of 100 characters.
public class User
{
[StringLengthValidation(50)]
public string Username { get; set; }
[StringLengthValidation(100)]
public string Email { get; set; }
}
Validating the Property using Reflection
This Validator
class uses reflection to find properties with the StringLengthValidationAttribute
. For each property, it retrieves the attribute instance using Attribute.GetCustomAttribute
. If the attribute is found, the property value is retrieved using property.GetValue
, and the IsValid
method of the attribute is called to validate the value. If validation fails, an error message is printed to the console, and the method returns false
. If all properties pass validation, the method returns true
.
using System;
using System.Reflection;
public class Validator
{
public static bool Validate(object obj)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
StringLengthValidationAttribute attribute = (StringLengthValidationAttribute)Attribute.GetCustomAttribute(property, typeof(StringLengthValidationAttribute));
if (attribute != null)
{
string value = property.GetValue(obj) as string;
if (!attribute.IsValid(value))
{
Console.WriteLine($"Validation failed for property {property.Name}. Maximum length is {attribute.MaxLength}.");
return false;
}
}
}
return true;
}
}
Usage Example
This example demonstrates how to use the custom attribute and the validator. It creates two User
objects, one with valid data and one with invalid data (usernames and emails exceeding the defined max lengths), and then uses the Validator
class to validate them. The results are printed to the console.
public class Example
{
public static void Main(string[] args)
{
User user1 = new User { Username = "johndoe", Email = "john.doe@example.com" };
User user2 = new User { Username = "thisusernameiswaytoolong", Email = "email.that.is.also.very.very.very.long@example.com" };
bool isValid1 = Validator.Validate(user1);
bool isValid2 = Validator.Validate(user2);
Console.WriteLine($"User 1 is valid: {isValid1}");
Console.WriteLine($"User 2 is valid: {isValid2}");
}
}
Concepts Behind the Snippet
This snippet illustrates the core concepts of custom attributes and reflection in C#. Attributes are metadata that can be associated with code elements (classes, properties, methods, etc.). Reflection allows you to examine and manipulate types, properties, methods, and other code elements at runtime. By combining these two concepts, you can create powerful and flexible validation mechanisms.
Real-Life Use Case
A real-life use case for custom attributes like this is data validation in a web API or a data access layer. You can define attributes to enforce specific rules on the data being passed into your application. This makes the validation logic declarative and easy to maintain. Another use case involves serialization and deserialization, where attributes dictate how objects are converted to and from different formats (e.g., JSON, XML).
Best Practices
Interview Tip
Be prepared to explain how custom attributes and reflection work, and provide examples of where they can be useful. Also, be aware of the performance considerations when using reflection. Interviewers might ask about alternatives to reflection for certain tasks.
When to Use Them
Use custom attributes when you need to add metadata to your code that can be processed at runtime. This is particularly useful for validation, serialization, and code generation scenarios where you want to avoid hardcoding logic directly into your classes or methods.
Memory Footprint
Custom attributes themselves have a relatively small memory footprint. However, the process of reflection can be more memory-intensive, especially if you are reflecting over a large number of types or properties. Caching reflected data can help mitigate this.
Alternatives
Alternatives to custom attributes and reflection include:System.ComponentModel.DataAnnotations
namespace for common validation scenarios.
Pros
Cons
FAQ
-
Can I apply multiple instances of the same custom attribute to a property?
Yes, you can apply multiple instances of the same custom attribute to a property if theAllowMultiple
property of theAttributeUsage
attribute is set totrue
. -
How can I access attribute data in code?
You can use theAttribute.GetCustomAttribute
orAttribute.GetCustomAttributes
methods to retrieve attribute instances from a type, property, method, or other code element. Then, you can access the properties of the attribute instance to retrieve the attribute data. -
Are custom attributes inherited?
The inheritance of custom attributes is controlled by theInherited
property of theAttributeUsage
attribute. If it's set totrue
, the attribute will be inherited by derived classes. Otherwise, it will not be inherited.