user712923
user712923

Reputation: 1555

How can I implement custom attributes in .NET 2.0?

Unfortunately I am still working on .NET 2.0. I have not created a custom attribute before. I want to create a CustomStringFormatAttribute:.

If a class, say Customer.Name, has:

MaxLength=30
ActualLength=10

I need to pad it with empty spaces till it reached 30.

I also need an attribute for date that I can format like DisplayDataFormat

I have created the following but How do I get access to the actual value of the property within the attribute?

public class Customer
{
    [CustomStringFormatAttribute(30)]
    public string Name { get; set; }

    //todo:customDateAttribute
    public DateTime StartDate { get; set; }
}

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public sealed class CustomStringFormatAttribute : Attribute
{
    private readonly int maxLength;

    public CustomStringFormatAttribute(int maxLength)
    {
       MaxLength = maxLength;
    }

    public  int MaxLength { get; private set; }

    //?Should I override ToString 
    public override string ToString()
    {
        return Format();
    }

    private string Format()
    {
        //simplified version of my formatting for brevity
        string source = "value from the property of the class.";//How do I get access to the actual value of the property within the attribute?
        const char paddingChar = ' ';
        return source.PadLeft(maxLength, paddingChar);
    }    
}

Any suggestions?

Note: I' used automatic property for brevity. I don't have that luxury in .NET 2.0.

Upvotes: 2

Views: 1062

Answers (2)

Salvatore Previti
Salvatore Previti

Reputation: 9080

Sorry, you cannot access the class instance or the property info inside your attribute. You should write an additional method, for example a static method in some "static" class, that allow you to do what you want to do.

Example....

    public static string FormatProperty(object instance, PropertyInfo property)
    {
        CustomStringFormatAttribute attrib = Attribute.GetCustomAttribute(property, typeof(CustomStringFormatAttribute)) as CustomStringFormatAttribute;
        return property.GetValue(instance, null).ToString().PadLeft(attrib.MaxLength, ' ');
    }

    public static string FormatProperty(object instance, string propertyName)
    {
        return FormatProperty(instance, instance.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance));
    }

But this is very uncomfortable and insanely slow since uses reflection to get property value through property info.

To access property attributes you need a PropertyInfo.

    public static int GetPropertyMaxLength(PropertyInfo property)
    {
        CustomStringFormatAttribute attrib = Attribute.GetCustomAttribute(property, typeof(CustomStringFormatAttribute)) as CustomStringFormatAttribute;
        return attrib != null ? attrib.MaxLength : int.MaxValue;
    }

    public static int GetPropertyMaxLength(Type type, string propertyName)
    {
        return GetPropertyMaxLength(type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance));
    }

Let's suppose we put these functions inside the attribute. Then we want to override, for example, our ToString method in our class Customer.

public override string ToString()
{
    return CustomStringFormatAttribute.FormatProperty(this, "Name");
}

The problem with this is of course Speed, it uses reflection by name, very slow, and Refactoring, you will not have a compile time warning or error if property "Name" doesn't exists, you will get only an exception at runtime. I would suggest you to use another mechanism.

With newer version of the language you could use lambda expressions to obtain your property info directly by the property itself, but since you are in C# 2.0 this is not possible.

Another solution can be: add instead another property called FormattedXXX, for example FormattedName that returns the lenght as you want it and you can use that property instead of the Name property. This will allow you to keep your formatted version of the property near your property.

Upvotes: 1

Allon Guralnek
Allon Guralnek

Reputation: 16131

You need to do it the other way around. You shouldn't have any logic in your attribute, it should simply expose properties with the information it contains (e.g. a MaxLength property). Then your Customer class should access the information provided by CustomStringFormatAttribute and format it accordingly:

private string m_Name;

public string Name
{
    get
    {
        var formatAttribute = typeof(Customer).GetCustomAttributes(false)
                                  .OfType<CustomStringFormatAttribute>
                                  .SingleOrDefault();

        if (formatAttribute != null)
            return m_Name.PadLeft(formatAttribute.MaxLength);

        return m_Name;
    }
    set
    {
        m_Name = value;
    }
}

Upvotes: 0

Related Questions