Article

A software component is an independent, reusable, binary piece that can be used by other programs. Essentially, a software component should be self-describing i.e. all its related information should be stored in the component itself, for e.g. what classes constitute a component, what it depends on etc. The deployment process of such a self-describing software component is simplified to a great extent. The programmers can easily write and maintain such components.

Consider the following declaration in C++ language:

private long empCode;

Here information is conveyed through a declaration. Now, if we need to associate a particular XML field with the variable empCode, the language doesn't provide any way to extend this declarative information. The rules, once predefined by the language designer, cannot be altered. We must devise our own way to declare and store such association in C++. Also, we must provide some method to access such information at runtime.

A possible workaround is to store such additional information in constants or separate resource files such as .IDL, .DEF, etc. However, this meta-information is loosely coupled with the corresponding member variable. Component-based programming doesn’t exactly fit into this model because the additional information associated with the component no longer resides in one place. Moreover, such workarounds don’t offer a general solution. Moreover, they are prone to errors.

The attributes in C# provide us the ability to attach additional information to the members of a class. The separate resource files are no longer required. An integral information of a type i.e. the relationship between the variable empCode and the XML field is stored along with the rest of the type's definition. This way the compiler and the runtime has complete knowledge of the relationship between these two. This allows us to build completely self-describing components. One can know everything about a component by examining the component itself.

.NET allows you to define your own custom attributes that are meaningful only to your applications. You need to define your new custom attribute class which must be derived, directly or indirectly, from the System.Attribute class. This class contains functionality that allows the easy and fast access of custom attributes from the assembly's metadata.

Attributes can have parameters in the same way as properties, methods, and constructors. The information stored in the attributes is passed in these parameters.

Attribute parameters can be of two types: Positional parameters or Named parameters. Positional parameters are mandatory and must be provided whenever the attribute is used. Also, they must be passed in a specific order and should be passed prior to named parameters, if any.

Named parameters are optional and the user of the attribute doesn't need to key in their value every time. They represent the information that remains unchanged in most situations. The programmer typically sets their default value in some field or property of the attribute class. If the user of the attribute supplies the value of a named parameter, it overrides its default value.

Consider a library application that uses the tables named Book, Student, etc. Now you need a way to store the DB table and column names info corresponding to the C# member fields. You can attach this info to the member fields by making use of the attributes. For this purpose, an attribute class is required. Let's define the DatabaseAttribute class derived from the System.Attribute class.

Most of the member fields in the library application’s source code correspond to the columns of the Book table. Therefore, make the Table property of DatabaseAttribute class a named parameter and set its default value to Book so that the user of the attribute doesn't need to key in the value most of the time. If the user wishes to provide some value other than Book, he/she can do so by using the named parameter as follows: Table="Student"

Whenever an attribute is used, if you want to force the user to specify a column name corresponding to a target member field, you need to make the Column property as the positional parameter. Hence, define a constructor for the DatabaseAttribute class that accepts a column name as a parameter. Note that the default value Book for the table name is initialized in the constructor itself.

The DatabaseAttribute is a user-defined custom attribute class

public class DatabaseAttribute : System.Attribute {

    string column, table;

    //Column is a read-only property so it cannot be used as a named parameter. If you want to use a property as a positional parameter, Microsoft’s Attribute Usage guideline states that you should declare it as read-only. Column property can be used as a positional parameter.
    public string Column {
        get { return column; }
    }

    //Table is a read/write property so it can be used as a named parameter
    public string Table {
        get { return table; }
        set { table = value; }
    }

    public DatabaseAttribute (string column) {
        this.column = column;
        this.table = "Book";
    }
}

The user of the DatabaseAttribute

class Student {
    //Attach the DatabaseAttribute to the field named code. studentCode is the corresponding column name. Table property is the named parameter and Student is its value.
    [Database("studentCode", Table="Student")]
    private int code;
    ...
}

Another user of the DatabaseAttribute

class Book {
    //Here optional named parameters are not passed; only mandatory information is passed.
    [Database("isbnCode")]
    private int code;

    [Database("author")]
    private string author;
    ...
}

.NET framework ships with a number of predefined attributes. It suffixes the predefined attribute names with the word 'Attribute' that helps us distinguish them from other .NET entities. You should follow the same convention (suffix the word 'Attribute' to the name of your attribute class) while defining the custom attributes. However, it is not mandatory to place this suffix during coding.

Although, SerializableAttribute is the actual name in .NET, [Serializable] is as good as [SerializableAttribute]

NonSerialized attribute when applied to a target field prevents it from being serialized.

[NonSerialized]
private ArrayList onlineUsers = new ArrayList();

For an attribute that accepts no parameters, parentheses are optional. Following are equivalent:

[NonSerialized]
[NonSerialized()]

For some target elements, you need to explicitly specify an element name along with an attribute. Examples of such elements are assembly and module.

Using the AssemblyKeyFileAttribute, you can associate the strong name with your assembly. You can delay-sign an assembly using the AssemblyDelaySignAttribute.

Multiple attributes can be specified in separate square brackets and placing them one on top of another. Attributes applied to the assembly and module elements must be placed at the top of the source file but after any using statements.

using System;
[assembly: AssemblyKeyFile("myStrongName.snk")]
[assembly: AssemblyDelaySign(true)]
class C { ... }