C# Attributes - Common and Custom

1. Attributes are a way of tagging methods and classes with specific information at code time, to make them behave differently at runtime. .Net uses them all the time to denote Webmethods, or ServiceContracts, or when importing DLLS. You can use the built-in ones, of define your own.
2. Create a new console application and create two methods:
    class Program
    {
        static void Main(string[] args)
        {
            SaySomething();
            SaySomethingBetter();
            Console.ReadKey(true);
        }

        static void SaySomething()
        {
            Console.WriteLine("Something");
        }

        static void SaySomethingBetter()    
        {
            Console.WriteLine("SomethingBetter");
        }
    }
Run the program and you see it prints both messages.
3. Mark one of the methods as Obsolete:
        [Obsolete()]
        static void SaySomething()
        {
            Console.WriteLine("Something");
        }
The program still runs, but you do get a wavy warning line under the call to the obsolete function, and when you hover the mouse, it tells you what the warning is for::
        static void Main(string[] args)
        {        'Program.SaySomething()' is obsolete 
            SaySomething();
            SaySomethingBetter();
            Console.ReadKey(true);
        }
4. You can put your own message in the Obsolete attribute:
        [Obsolete("Don't use this, fool!")]
        static void SaySomething()
        {
            Console.WriteLine("Something");
        }
Which is the same as before, except the hover message uses your text:
        static void Main(string[] args)
        {        'Program.SaySomething()' is obsolete: 'Don't use this, fool!' 
            SaySomething();
            SaySomethingBetter();
            Console.ReadKey(true);
        }
5. Another useful attribute is the Conditional (you will have to import the System.Diagnostics namespace):
        [Conditional("DEBUG")]
        static void SaySomething()
        {
            Console.WriteLine("Something");
        }
When you run this in 'Release' mode, then the method is not executed. This gives a good way of putting in code which should only run when debugging, such as Logging or Diagnostic messages.
6. You can define your own conditions for execution by using compiler directives. At the top of the file, put the following:
     #define MYCONDITION
Then change the condition in the attribute:
        [Conditional("MYCONDITION")]
        static void SaySomething()
        {
            Console.WriteLine("Something");
        }
When you run this, the method is executed. Change the define line to:
     #undef MYCONDITION
Then run the program again - the method is not run. This means you can turn on and off certain methods in the program with a simple compiler directive at the top. Omitting the #define altogether has the same effect as #undef, i.e. the code won't run.
7. You are not restricted to the built-in attribues. You can define your own, e.g.
    [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct)]
    public class Author : System.Attribute
    {
        private string name;
        public double version;

        public Author(string name)
        {
            this.name = name;
            version = 1.0;
        }

        public string GetName()
        {
            return this.name;
        }
    }
The AttributeUsage attribute allows you to restrict the usage of the attribute to specific types of object, in the above example, this can only be applied to Classes and Structs (and not to methods or return values, for instance). You can implement as many fields, properties and methods as you might need. We implement a GetName method, which returns the name field, since we'll need it later.
8. Now you can decorate your main Program class with it (note, not your methods, since we didn't allow it above!):
    [Author("Jimmy Mac", version=1.1)]
    class Program
    {
        static void Main(string[] args)
        {
            ...
            ...
        }
        ... etc ...
    }
The thing to note is that the first parameter in the declaration is always the first field in the class, i.e. the name in this case. All other fields or properties can be defined as a name=value pair comma separated, e.g. version=1.1
9. Being able to set attributes in this way is interesting, but unless the methods themselves have access to what you defined, they won't be able to modify their behaviour, and this is the main point of Attributes - to change the way something decorated with them behaves.

Unfortunately, attributes don't exist until we actually ask for them, and we have to scan the entire object to find them.. I've written a small script which is re-usable for this scanning:

    public static System.Attribute GetAttribute(System.Type ContainerClass, System.Type SearchClass)
    {
        System.Attribute retAtt = null;
        System.Attribute[] attrs = System.Attribute.GetCustomAttributes(ContainerClass);
        foreach (System.Attribute attr in attrs)
        {
            if (attr.GetType() == SearchClass)
            {
                retAtt = attr;
            }
        }
        return retAtt;
    }
This method, scans the outer object (which in our case would be the Program class) for an instance of the Attribute, then loops around any custom attributes found until it finds the one we're looking for (in this case Author)
10. Now we can access the Author tag from one of our methods, by calling the method and casting the result into the type of Attribute we want:
        static void SaySomething()
        {
            Author me = (Author)GetAttribute(typeof(Program),typeof(Author));
            Console.WriteLine("Author: " + me.GetName());
            Console.WriteLine("Version: " + me.version);
        }
Our method can then call the fields and methods of our Attribute class, such as GetName() and version, and behave differently according to their values. We could, for instance, use a different data store (file or database) depending on a custom attribute being set or not.
And that is all there is to Attributes. You can use the built-in ones like Obsolete or Conditional, or create your own custom attributes and access the values at runtime.