Understanding Attributes in C#

Attributes are widely used in .Net libraries such as XML serialization. The syntax of attributes is very confusing until you realize that attributes are actually classes. This article provides a quick guide to understanding attribute syntax, how to read it, and how to look up an attribute documentation to be able to spcify them in your programming practice.

This article is not a thorough reference on attributes but a quick stab at the main features of attributes so that you can gain an understanding of attributes and start using them after the first read.

Attributes are used all over the place in .Net. Let us consider an XML serialization example where attributes take an important and necessary role. In this example we will be designing a c# class and stream it as XML. The c# class definition follow:


	Public class MyClass
	{
		public string myField = "abc";
	}

The above class can be streamed as XML as follows


	MyClass myObject = new MyClass;
	XmlSerializer  xs = new XmlSerializer(typeof(myObject));
	StringWriter sw = new StringWriter();
	Xs.serialze(sw,myObject);

This will produce an XML as follows


<MyClass>
	<myField>abc</myField>
</MyClass>

Let us now introduce attributes so that the generated XML can be tailored


	[XmlRoot("MyXmlClass")]
	Public class MyClass
	{
		[XmlElement(ElementName="myXmlField")]
		public string myField = "abc";
	}

This will produce an XML as follows


<MyXmlClass>
	<myXmlField>abc</myXmlField>
</MyXmlClass>

There are lot more attributes in XML serialization to control the serialization process. You can see these attributes in the references section of this article. But for now these two attributes are sufficient for our discussion.

When I have encountered these attributes first my first problem was figuring out how to read this attribute specification. What is the meaning of "[XmlRoot("somename")]"? I could guess that they are trying to set the name of the root node of the XML. But the syntax is unclear because in one case they are specifying a string directly inside quotes, in another case they are using some sort of a key to specify the string against.

Some times they use commas to separate multiple attributes, and sometimes they stack them. Sometimes there are repeated attributes and sometimes not. Sometimes they seem to be applied to fields and sometimes to classes. It seemed to be a very inconsistent set of rules.

But it turns out there is a mehtod to this madness afterall. The key to understanding to attributes lies in the fact that they represent .Net classes (first class citizens of the .net world). Let us investigate this aspect of attributes for a second.

Attributes are classes

When you specify an attribute you are actually allowing the .net to instantiate a class represented by that attribute. Because of this the specification of an attribute resembles the syntax of a class constructor where all the arguments to the constructor are static and specified at compile time.

Attribute names are class names

Because an attribute is a class, an attribute's name is a classname. A classname includes a namespace and so does an attributes name. So what class does "XmlRoot" represent? This name is a short cut for "System.xml.serialiation.XmlRootAttribute". I will show you later how this long name shrinked to a just "XmlRoot".

Attributes use name spaces

Because attributes are classes you need to specify the "using" clause to import the namespace they are part of. So in the above case by importing the "System.xml.Serialization" namespace you can shorten the name to "XmlRootAttribute".

Attributes have multiple constructors like classes

Because an attribute specification is the specification of a constructor, what goes between ( and ) are the arguments to that constructor. Like a regular class an attribute can have more than one constructor.

Attribute's constructors does something more

An exception to the above construction rule is that the attribute specification allows you not only to construct a class but also set its public properties. This means that if the class has some public properties, the specification needs to accommodate a way to set these properties as well. The construction syntax is extended to manage this need. The public properties are named and specified following the regular arguments usinga key/value pair syntax

Attributes constructor invocation can take multiple forms

The specification of an attribute can take multiple forms, all leading to it's construction and setting of its public properties. Doesn't matter how you end up specifying an attribute you derive a final fully qualified construction specification for the attribute where you will have the constructor arguments followed by the optional public properties.

How to look up attribute documentation

Documentation of an attribute (because ití»s a class) is available just like the documentation of any other c# class. You look up the arguments of its constructor and you look up the public properties of the attribute class. An attribute documentation in addition will also specify what programming constructs for which this attribute is applicable.

Dealing with attributes's public properties

Public properties of an attribute class are set using key/value pair combinations followed by the mandatory arguments of its constructor.

Multiple styles of specifying attributes

While encountering attributes the thing that throws you off the most is the varieties of syntax that can be used to specify an attribute. Let me address the common usage patterns of specifying an attribute

Usage


[namespace.attributeNameAttribute](arg1,arg2,param1=value,param2=value);
[attributeNameAttribute](arg1,arg2,param1=value,param2=value);
[attributeName](arg1,arg2,param1=value,param2=value);

The first line identifies a fully qualified class name of an attribute followed by the constructor arguments, followed by the public properties. The second line omits the namespace from the fully qualified name. The third line omits the "Attribute" suffix from the class name. This is purely a convenience offered by the programming facilities. Let us see some examples demonstrating these variations.

Examples


1. [System.Xml.Serialization.XmlRootAttribute(ElementName="LOAD_TENDER_TRIP", Namespace="", IsNullable=false)]
2. [System.Xml.Serialization.XmlRootAttribute("LOAD_TENDER_TRIP", Namespace="", IsNullable=false)]
3. [XmlRootAttribute(ElementName="LOAD_TENDER_TRIP", Namespace="", IsNullable=false)]
4. [XmlRoot(ElementName="LOAD_TENDER_TRIP", Namespace="", IsNullable=false)]

Commentary on examples

  • 1 is allowed because this attribute has a default constructor and three public properties which are set in that line
  • 2. is allowed because this attribute also has a constructor that takes one argument. Two public properties are set in addition in the same line
  • In line 3 the namespace of the class is ommited, but requires that you import the name space with "using System.Xml.Serialization"
  • In line 4 the "Attribute" suffix is ommited.

Attribute Specification rules

It is time to summarize the various rules governing the attributes

  1. attributes are specified inside of [] and usually specified right above the programming construct to which they belong. The common programming constructs are field names, method names, class names etc.
  2. The name of an attribute is the name of its class
  3. If the namespace is imported, you can just use the unqualified class name of the attribute while specifying it. This is similar to the usage of specifying a class name where you can omit the namespace as long as the namespace is in scope.
  4. It is a convention to end the attribute class names with "Attribute". This is optional, although recommended
  5. The suffix "Attribute" can be ommited from the specification. .Net will add this suffix to locate the class. If the original class does not have an "Attribute" suffix then the name is used as it is to look for the class.
  6. You can have attributes stacked up one on top of the other to specify multiple attributes to the same programming construct. Example
    
    [attribute1()]
    [attribute2()]
    
  7. Attributes can be lined up with a comma separator inside of []. Example
    
    [attribute1(), attribute2()]
    
  8. Some attributes allow multiple instances and some don't. Compiler will give an error if it is set non-multiple and if you try to repeat it
  9. constructor arguments are followed by optional public property specifications. If there is a default constructor then you can omit the brackets. Example
    
    [attribute1]
    
  10. The IDE knows to go to the right help section for that attribute class even if the attribute name is in its short form (sans "attribute" and sans "namespace")

References

  1. "Programming C#: Attributes and Reflection" by Jesse Liberty at O'Reilly This is an excellent and fairly complete reference on the subject
  2. "Attributes that Control XML Serialization" in .Net Framework Developers Guid. This reference lists a the complete set of XML attributes that are used in XML serialization.
  3. "Controlling XML Serialization Using Attributes" in .Net Framework Developers Guid This reference goes into the detail process of effectively using these attributes to serialize such elements as arrays and collections