C# Custom Attributes
What is a custom attribute?
C# has things called attributes that are used to give more information to the program or compiler about things like properties, methods, etc. One example of an attribute is the Serializable attribute which is used to mark a class as being serializable for remoting. The Serializable attribute can be applied to almost any class and it automatically makes it able to be sent across a remoting connection. Here is an example of how it would be applied to an object.
using System;
[Serializable]
public class MyClass
{
}
A custom attribute is different to an attribute in that it is one that is made by a programmer for their own purpose. It is up to the programmer to write the code that performs what the attribute is supposed to do unlike with the ones that already exist in C#. Custom attributes are very powerful because they give you the ability to examine the code that you create at runtime which means you can simplify a whole lot of things.
Creating the custom attribute class
We are going to create a Customer data class as an example of something you would do often in a data application. We are going to create a custom attribute to mark the data fields that are saved in the database so as to differenciate them from other fields in the class.
First we will create the custom attribute class called DataFieldAttribute. When you create a custom attribute you must inherit from the Attribute class. Here is the code.
using System;
public class DataFieldAttribute : Attribute
{
}
Applying the custom attribute
Now we will create the Customer data class that the DataFieldAttribute will be applied to. We are just going to use a very simple example of a Customer class that only has the first name, last name and a method for getting the full name. Our goal is to apply the DataField attribute to the first name and last name fields because they are the only ones that must be saved in the database. Here is the Customer class code.
using System;
class Customer
{
public string FirstName;
public string LastName;
public string FullName
{
get { return FirstName + " " + LastName; }
}
}
The interesting thing about attributes is that when you create the attribute class you have to add the word Attribute to the end of the name of the class but when you apply the attribute to something then you have to use it without Attribute added to the name. Here is the same code from above with the DataField attribute applied to the data fields.
using System;
class Customer
{
[DataField]
public string FirstName;
[DataField]
public string LastName;
public string FullName
{
get { return FirstName + " " + LastName; }
}
}
Using the custom attribute
Now we can finally get round to using the attribute. To do this we will have to instantiate a Customer object and set its values. We are not going to include all the database stuff so you will just have to imagine that it is happening. After that we will loop through all the data fields and get their values and display them to see if the DataField attribute works properly. Here is the code.
// Instantiate Customer and initialize it
Customer cust = new Customer();
cust.FirstName = "John";
cust.LastName = "Smith";
// Get all members of the Customer class and loop through them
System.Reflection.MemberInfo[] CustomerMembers = cust.GetType().GetMembers();
foreach (System.Reflection.MemberInfo mi in CustomerMembers)
{
// Get the custom attributes on the member for DataFieldAttribute only
object[] attributes = mi.GetCustomAttributes(typeof(DataFieldAttribute), true);
// If this member has at lease one DataFieldAttribute then display it to the user
if (attributes.Length > 0)
{
System.Windows.Forms.MessageBox.Show(mi.Name);
}
}
This is a very simple example but from it you should be able to figure out how to adapt it to suit the situations that you come across.
Parameters
You can give your attributes parameters by creating a constructor for your attribute class. Here is an example of adding a constructor to the DataFieldAttribute class and then an example of using the parameter in the Customer class.
using System;
public class DataFieldAttribute : Attribute
{
public DataFieldAttribute(string DatabaseFieldName)
{
}
}
using System;
class Customer
{
[DataField("FName")]
public string FirstName;
[DataField("LName")]
public string LastName;
public string FullName
{
get { return FirstName + " " + LastName; }
}
}
Here is the code based on an example higher up which you can use to get the value of the parameter.
// Instantiate Customer and initialize it
Customer cust = new Customer();
cust.FirstName = "John";
cust.LastName = "Smith";
// Get all members of the Customer class and loop through them
System.Reflection.MemberInfo[] CustomerMembers = cust.GetType().GetMembers();
foreach (System.Reflection.MemberInfo mi in CustomerMembers)
{
// Get the custom attributes on the member for DataFieldAttribute only
object[] attributes = mi.GetCustomAttributes(typeof(DataFieldAttribute), true);
// If this member has at lease one DataFieldAttribute then display it to the user
if (attributes.Length > 0)
{
DataFieldAttribute dfa = (DataFieldAttribute)attributes[0];
System.Windows.Forms.MessageBox.Show(dfa.DatabaseFieldName);
}
}
Restricting usage
You can restrict what your attribute can be applied to by using the AttributeUsage attribute on your attribute. Here is an example of how to restrict the attribute to being used with only fields.
using System;
[AttributeUsage(AttributeTargets.Field)]
public class DataFieldAttribute : Attribute
{
}
using System;
class Customer
{
[DataField] // Allowed
public string FirstName;
[DataField] // Allowed
public string LastName;
[DataField] // NOT Allowed
public string FullName
{
get { return FirstName + " " + LastName; }
}
}