Custom Attributes in C#
An attribute in C# is a way for you to add meta-data around an element in your code. You can attach an attribute to many different targets, see here for full list.Attributes are useful in supplying extra information about the target at all stages of developement, from design to run time. They help you specify specific information about the target, such as the method type, such as designating a WebMethod. Or to inform the compiler that a method is now obsolete, so that the compiler can warn you that you are using an outdated method. I think the most recognizable one that I think most people would have used an not even thought about it as an attribute is the Serializable.
You can also create your own custom attributes in C#, and basically all it takes is a small lightweight class that inherits from System.Attribute and has a constructor and some public properties. These properties can be then read at runtime through reflection.
In this example, we are going to create an attribute to denote a field that we can expect in an import from a user. So first we create the attribute. We are going to require that we have 3 pieces of meta-data in our attribute, a display name, whether or not the field is required in the upload, and a description of the field (used for display purposes). So let's create the class
As you can see we inherited from System.Attribute, and we are using an attribute called System.AttributeUsage (msdn), that tells us how this attribute is going to be used.
using System;
using System.Collections.Generic;
using System.Text;
[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)]
public class ImportDescriptor : System.Attribute{
/// <summary>
/// Custom Property Attribute
/// </summary>
/// <param name="displayName">The string to be displayed in the Import UI (e.g. grid column) for this property.
/// <param name="required">If true, a value for this property is required for import. If false, the property value is optional.
public ImportDescriptor(string displayName, string description, bool required) {
DisplayName = displayName;
Required = required;
Desc = description;
}
public string DisplayName{
get { return (_displayName); }
set { _displayName = value; }
} private string _displayName = null;
public bool Required{
get { return (_required); }
set { _required = value; }
} private bool _required = false;
public string Desc{
get { return (_desc); }
set { _desc = value; }
} private string _desc = null;
}
Now we need to add that attribute to the properties on our class that we expect to see in an import. Assume we have a class Person that has properties of PersonID, FirstName, LastName, and Title. And lets assume that everything except title is required. So lets add the attributes to the Person class.
We now have our class set up to use our attribute. We can add more fields to our Person class and add the ImportDescriptor attribute and our code will automatically expect to see the field in the import. So our framework is set up. We now just need to consume this. Below is an example of how to use reflection to read the attribute, and handle the line in the import accordingly. It is a brief example and will leave the rest of the import code as an exercise for you. In this example I am using reflection to read the attributes and store them in a class (ImportProperties) that we are using for the actual import. The variable _dataType is of type Object. I wanted this to be as generic as possible so that I could use this for any type of import, not just for the Person Class.
public class Person {
private int _personID;
private string _first_name;
private string _last_name;
private string _title;
public Person() {
//empty constructor
}
[ImportDescriptor("Person ID", "ID of the person", true)]
public int PersonID {
get { return _personID; }
set { _personID = value; }
}
[ImportDescriptor("First Name", "First Name of the pesron.", true)]
public string FirstName {
get { return _first_name; }
set { _first_name = value; }
}
[ImportDescriptor("Last Name", "Last Name of the person", true)]
public string LastName {
get { return _last_name; }
set { _last_name = value; }
}
[ImportDescriptor("Title", "The persons title in the company (CEO, Developer, etc.)", false)]
public string Title {
get { return _title; }
set { _title = value; }
}
}
private ListGetAttributes(Object dataType) {
PropertyInfo[] props = dataType.GetType().GetProperties();
PropertyInfo prop;
List<importproperties> lst = new List<importproperties>();
for (int i = 0; i < props.GetLength(0); i++) {
prop = props[i];
if (prop.GetCustomAttributes(typeof(ImportDescriptor), false).Length > 0) {
ImportDescriptor importDesc = (ImportDescriptor)prop.GetCustomAttributes(typeof(ImportDescriptor), false).GetValue(0);
ImportProperties import = new ImportProperties();
import.DisplayName = importDesc .DisplayName;
import.Desc= importDesc.Desc;
import.Required = importDesc .Required;
lst.Add(import);
}
}
return lst;
}
Since you can have multiple attributes per target we are only going to be looking for the ImportDescriptor, you can extend this to read all attributes.
Also, since this is using reflection and a list, you need to make sure you are using System.Reflection, and System.Collections.Generic in order to use PropretyInfo.
Labels: .NET
posted by Tom Becker at
9/21/2009
![]()

0 Comments:
Post a Comment
<< Home