Quick Overview of C# Attribute Programming






4.61/5 (16 votes)
Programming with attributes refines and solves some of the difficult development problems we face every day.
Introduction
Attribute programming is a declarative programming model tool that you should keep in your development toolbox. Programming with attributes refines and solves some of the difficult development problems we face every day.
Background
While this article is squarely aimed at beginners, the reader must have an understanding of Reflection in order to comprehend attribute programming.
C# Attribute Programming
When you see this property declaration:
[CustomAttribute]
public string MyProperty { get; set; }
...you should recognize a property declaration that has been 'decorated' with the custom attribute named CustomAttribute
.
There are a few things going on here.
First of all, regardless of whether you've ever created your own attributes and used them in your code, you must have seen various .NET Framework-related attributes at certain times in your applications. For instance, if you generate a class from an XSD schema, you'll notice a bunch of attributes attached to almost all the classes in the resultant source file.
Why are they there? What do they do?
At runtime, the .NET Framework investigates those attributes and executes code based on the values those attributes contain.
How does the runtime investigate those attributes?
Reflection.
Now, why use attributes?
Basically, the answer is to simplify your programming and make your code more readable by making more of your code "declarative" in nature. Attribute programming is a declarative programming model.
Attribute programming is effective when some class or class member needs to know something about itself at runtime, and it would make for very inelegant or clumsy code to feed that data to the class or member in any other way.
An example: a class is "hydrated" from a text file at runtime. The name of the text file is known at design time. The class needs a way to know what text file to read at runtime to hydrate itself.
There would be many ways to solve this problem. Among them, a popular one would be to add a property like ContentFileName
to the object and hard-code in the name of the file. That would work but it's clumsy and unsightly.
At runtime, that class is going to hydrate itself and become, essentially, data. The name of the file to hydrate with is data. So we're dealing with "data about data", also called "metadata". Whenever you're dealing with metadata, think about attribute programming.
A better solution is to create your own custom attribute. It must inherit from System.Attribute
, or another class that itself inherits from
Attribute
.
A typical custom attribute could look like:
public class ObjectTextAttribute : Attribute
{
}
Now back in your object code, do this:
public class ObjectTextAttribute : Attribute
{
public ObjectTextAttribute()
{
}
public string ContentFileName { get; set; }
}
[ObjectTextAttribute(ContentFileName="blah.txt")]
public class TextFile
{
}
You're about halfway done now. You've used attribution to tell that class to hydrate itself from the text file named "blah.txt". Other class definitions could hydrate themselves from different text files.
What's left is the code to parse out those attributes and to do something with them.
Reflection saves the day.
A typical solution for this is to create something like a ReflectOnAttributes
method in this class, or better yet,
a base class that this class is derived from.
In the ReflectOnAttributes
method, you would use reflection code to find and isolate the type of attribute you're looking for,
and then do something once you found the value(s).
In this case, we'll scan for the MyCustomAttribute
value and then parse out the ContentFileName
once found. When we have that, we'll read the text in from that
file and assign it to a property in our class.
using System;
using System.IO;
namespace ClassLibrary
{
public class MyCustomAttribute : Attribute
{
public String ContentFileName { get; set; }
}
[MyCustomAttribute(ContentFileName = "C:\blah.txt")]
public class MyCustomClass
{
public MyCustomClass()
{
ReflectOnClassAttributes();
}
public String BodyText { get; private set; }
private void ReflectOnClassAttributes()
{
//inspect for the custom attribute and obtain value of property desired
object[] classAttrs = this.GetType().GetCustomAttributes(typeof(MyCustomAttribute), true);
if ((classAttrs != null) && (classAttrs.Length > 0))
{
//inspect for the body attribute, this gives our content for the main body
MyCustomAttribute attr = (MyCustomAttribute)classAttrs[0];
string fileName = attr.ContentFileName;
BodyText = File.ReadAllText(fileName);
}
}//method
}//class
}//namespace
There's a lot going on in the (above) code. Let's take it step-by-step.
We start out by defining an attribute class called MyCustomAttribute
. It must inherit from the built-in .NET Framework
System.Attribute
class.
Next we define a class that will use MyCustomAttribute
. MyCustomClass
is decorated with a citation to "MyCustomAttribute
",
and provides a value in the attribute constructor to the property ContentFileName
. This is where we give the class knowledge about which text
file to open and read and hydrate itself.
When the class is instantiated and ReflectOnClassAttributes()
is called, the
Reflection process
interrogates the class declaration and determines what, if any, attributes are declared. When it finds the MyCustomAttribute
, it digs into the property and
gets its value. Then it simply reads the text file into the BodyText
property.
This is a simple example of attribute programming, and you can take it further in your own work. And you should, because attribute programming resolves and refines many of the programming problems we're faced with on a daily basis.
History
Submitted May 22 2012.