Generic Attributes in C#

As awesome as C# is, once you want to do some more advanced stuff with attributes, you quickly run into several limitations. There is even a Microsoft Connect entry on this issue.

  • Attributes can’t be generic, since a generic type cannot derive from ‘Attribute’.
  • An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.

Using TypeDescriptor and a whole bunch of custom code these limitations can be overcome. In this post I will describe an easier straightforward way by which to create something similar to generic attributes, ignoring the second issue for now. In future posts I will discuss how and where these attributes can be used for something I previously called attribute metabehavior.

The idea is quite straightforward; although attributes can’t be generic, nothing prevents you from adding generic instances to them. These instances can later be extracted at run time using reflection. The trick of course is initializing the generic instance. Once you know the Activator class can create instances by knowing their Type definition, the solution becomes self-evident. By simply passing the generic type as an argument to the attribute, instance creation can be delegated to Activator. Optional constructor arguments can also be passed as attribute arguments.

public class BehaviorAttribute : Attribute
{
	/// <summary>
	///   The dynamically created instance of the type passed to the constructor.
	/// </summary>
	public object DynamicInstance { get; private set; }

	/// <summary>
	///   Create a new attribute and initialize a certain type.
	/// </summary>
	/// <param name = "dynamicType">The type to initialize.</param>
	/// <param name = "constructorArguments">
	///   The arguments to pass to the constructor of the type.
	/// </param>
	public BehaviorAttribute(
		Type dynamicType,
		params object[] constructorArguments )
	{
		DynamicInstance =
			Activator.CreateInstance( dynamicType, constructorArguments );
	}
}

This attribute can then be be applied as follows:

class Answer<T>
{
	public T Value;

	public Answer( T value )
	{
		Value = value;
	}
}

[BehaviorAttribute( typeof( Answer<int> ), 42 )]
class TheWorld {}

Using ordinary reflection, the instance can be extracted.

Type type = typeof( TheWorld );
var behavior = (BehaviorAttribute)type
    .GetCustomAttributes( typeof( BehaviorAttribute ), false ).First());
var genericInstance = (Answer<int>)behavior.DynamicInstance;
int answer = genericInstance.Value;

What follows is up to you, how will you use this? I’ll describe a few advanced use cases in subsequent posts.

About these ads
  1. #1 by Neige on December 16, 2013 - 12:35 pm

    Hello,

    That help me a lot, but i have a question. If the Attribue property is a “struct” like “Color” ?

    Here my example :

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    public class TableAttribute : Attribute {
    public bool HasBorder;

    public System.Windows.Forms.HorizontalAlignment HeaderTextAlign;
    public Color? HeaderBackColor;
    public Color? HeaderForeColor;
    public Font HeaderFont;

    public System.Windows.Forms.HorizontalAlignment TextAlign;
    public Color? BackColor;
    public Color? ForeColor;
    public Font Font;
    }

    FYI this Class Attribute help me to generate “table” in lot of format (html, word, jpeg …).

    • #2 by Steven Jeuris on December 16, 2013 - 12:49 pm

      Your question is unclear, but I suppose you are stuck trying to initialize a Color from within the Attribute definition? This is not possible due to the second restriction I listed. You will need to do some kind of conversion in the constructor instead and thus a general solution won’t be possible. One possibility is passing 4 int arguments representing ARGB. I’m still exploring a more general approach to initializing such objects, but it will always need to rely on a similar implementation. A general solution could involve some sort of parser, aware about all the basic FCL types.

  2. #3 by Gabriel Battista on May 14, 2014 - 6:03 pm

    Hi there, thanks for the info.
    Personaly I’m on an Asp.Net Mvc4 proyect.
    I’m trying to do something like this on my Secure, so I donot have to deploy everytime that some new role is added to my app.

    [Authorize()]
    [Secure(Roles = WebUI.Controllers.ActionController.Ver() )]
    public class ActionController : Controller
    {

    public static string Ver(){
    return “Developers”;
    }

    And I get this….

    Error 1 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type

    • #4 by Steven Jeuris on May 15, 2014 - 11:32 am

      ….. soooo, what is your question? I presume you gave the answer yourself? “An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type”?

      • #5 by Gabriel Battista on May 15, 2014 - 12:41 pm

        Thats the deal…. I had tey const, and didnot work. Ans ot cant be a const. I need it to be method so I can retrive from my db how has access tp this controller.
        Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: