Reputation: 1555
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
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
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