Is it possible to query custom Attributes in C# during compile time ( not run-time )

Yordan Georgiev picture Yordan Georgiev · Apr 15, 2009 · Viewed 8.3k times · Source

In other words could it be possible to create assembly, which does not even compile (assuming the checking code is not removed ) if each one of the Classes does not have ( "must have" ) custom attributes ( for example Author and Version ) ?

Here is the code I have used for querying during run time :

using System;
using System.Reflection;
using System.Collections.Generic; 


namespace ForceMetaAttributes
{

    [System.AttributeUsage ( System.AttributeTargets.Method, AllowMultiple = true )]
    class TodoAttribute : System.Attribute
    {
        public TodoAttribute ( string message )
        {
            Message = message;
        }
        public readonly string Message;

    }

    [System.AttributeUsage ( System.AttributeTargets.Class |
        System.AttributeTargets.Struct, AllowMultiple = true )]
    public class AttributeClass : System.Attribute
    {
        public string Description { get; set; }
        public string MusHaveVersion { get; set; }


        public AttributeClass ( string description, string mustHaveVersion ) 
        {
            Description = description; 
            MusHaveVersion = mustHaveVersion ; 
        }

    } //eof class 


    [AttributeClass("AuthorName" , "1.0.0")]
    class ClassToDescribe
    {
        [Todo ( " A todo message " )]
        static void Method ()
        { }
    } //eof class 

    //how to get this one to fail on compile 
    class AnotherClassToDescribe
    { 

    } //eof class 

class QueryApp
{
        public static void Main()
        {

                Type type = typeof(ClassToDescribe);
                AttributeClass objAttributeClass;


                //Querying Class Attributes

                foreach (Attribute attr in type.GetCustomAttributes(true))
                {
                        objAttributeClass = attr as AttributeClass;
                        if (null != objAttributeClass)
                        {
                                Console.WriteLine("Description of AnyClass:\n{0}", 
                                                                    objAttributeClass.Description);
                        }
                }



                //Querying Class-Method Attributes  

                foreach(MethodInfo method in type.GetMethods())
                {
                        foreach (Attribute attr in method.GetCustomAttributes(true))
                        {
                                objAttributeClass = attr as AttributeClass;
                                if (null != objAttributeClass)
                                {
                                        Console.WriteLine("Description of {0}:\n{1}", 
                                                                            method.Name, 
                                                                            objAttributeClass.Description);
                                }
                        }
                }
                //Querying Class-Field (only public) Attributes

                foreach(FieldInfo field in type.GetFields())
                {
                        foreach (Attribute attr in field.GetCustomAttributes(true))
                        {
                                objAttributeClass= attr as AttributeClass;
                                if (null != objAttributeClass)
                                {
                                        Console.WriteLine("Description of {0}:\n{1}",
                                                                            field.Name,objAttributeClass.Description);
                                }
                        }
                }
                Console.WriteLine ( "hit Enter to exit " );
                Console.ReadLine ();
        } //eof Main 
} //eof class 

} //eof namespace 


//uncomment to check whether it works with external namespace 
//namespace TestNamespace {

//  class Class1 { }
//  class Class2 { }

//}

Edit: Just to justify my choice for answer. I think casperOne provided the correct answer of the question.

However the reasons for asking the question seemed to be weak. Probably I should start to use some external tool such as : FinalBuilder or create unit tests checking for this "requirement", using Pex , Nunit or other unit testing frameworks ...

EDIT I added a small code snippet of a console program at the end of the answers that performs the check ... feel free to comment, criticize or suggest improvements
Once more I realized that this "requirement" should be implemented as part of the unit testing just before the "check in"

Answer

casperOne picture casperOne · Apr 15, 2009

No, it is not possible to hook into the compilation of the assembly and check if it exists.

However you can hook into the build process, which is made up of more than just running the compiler. You could create a custom MSBUILD task (or NAnt, if you are using that) which checks the assembly through reflection after it is built and then fail the build if it doesn't have the required attributes.

Of course, you should probably still verify this in code as well. What you are trying to do is not a good substitute for a proper runtime check.