Efficiently Accessing Custom Attributes on Enum Values in C#
If you are developing applications in C#, you might sometimes encounter a need to fetch custom attributes associated with enum values. This task may appear straightforward, especially for those unfamiliar with reflection and IL code generation. However, simply converting enum values to strings (their names) can often lead to performance issues. In this blog post, we’ll explore a clean, efficient way to retrieve these attributes without the hassle of string conversions.
Understanding the Problem
Let’s start with a quick example where we define an enum adorned with custom attributes:
public enum MyEnum {
[CustomInfo("This is a custom attrib")]
None = 0,
[CustomInfo("This is another attrib")]
ValueA,
[CustomInfo("This has an extra flag", AllowSomething = true)]
ValueB,
}
In this snippet, we have an enum named MyEnum
, which contains multiple values, each paired with a CustomInfo
attribute detailing additional information related to each enum member.
The Challenge
The primary challenge arises when we need to retrieve these CustomInfo
attributes from an instance of the enum. The initial method that comes to mind is using the following code:
public CustomInfoAttribute GetInfo(MyEnum enumInput) {
Type typeOfEnum = enumInput.GetType();
FieldInfo fi = typeOfEnum.GetField(enumInput.ToString());
return fi.GetCustomAttributes(typeof(CustomInfoAttribute), false).
FirstOrDefault() as CustomInfoAttribute;
}
While this function effectively retrieves the attribute, it suffers from performance inefficiencies due to the use of enumInput.ToString()
, which converts the enum value to its string representation.
A Better Approach: Using IL Code Generation
Fortunately, there’s a more efficient method to access custom attributes without converting enums to strings using IL code generation. By employing dynamic methods and the ILGenerator
, we reduce overhead and improve performance. Below, we outline how to create a delegate that helps in accessing properties swiftly, which can be adapted for attribute retrieval as well.
Steps to Implement IL Code Generation
-
Create a Delegate: Define a delegate that takes an object and returns an object. This will allow us to create a fast property getter.
public delegate object FastPropertyGetHandler(object target);
-
Emit IL: Using the
ILGenerator
, we can generate an IL method that loads the target object onto the stack, calls the getter method, and handles the return value properly.private static void EmitBoxIfNeeded(ILGenerator ilGenerator, System.Type type) { if (type.IsValueType) { ilGenerator.Emit(OpCodes.Box, type); } }
-
Get Property Getter: Now, we create a method to generate a dynamic method and build the necessary IL code to create a fast property getter:
public static FastPropertyGetHandler GetPropertyGetter(PropertyInfo propInfo) { DynamicMethod dynamicMethod = new DynamicMethod( string.Empty, typeof(object), new Type[] { typeof(object) }, propInfo.DeclaringType.Module); ILGenerator ilGenerator = dynamicMethod.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod(), null); EmitBoxIfNeeded(ilGenerator, propInfo.PropertyType); ilGenerator.Emit(OpCodes.Ret); FastPropertyGetHandler getter = (FastPropertyGetHandler)dynamicMethod.CreateDelegate(typeof(FastPropertyGetHandler)); return getter; }
Conclusion
By implementing the method outlined above, you can effectively reduce the overhead associated with attribute retrieval from enum values. Not only does this approach eliminate unnecessary string conversions, but it also produces faster runtime performance—essential for applications that demand efficiency.
Incorporating these techniques into your C# projects will help you navigate enum attributes smoothly and allow for cleaner, more maintainable code. Happy coding!