I got myself in a situation where using the System.Attribute class seemed (at first glance) to be a good idea.
I have an object to be printed in my application, and I need a label before each property (or just a string before it). I could put each property hardcoded like:
Console.WriteLine("Color:"+obj.color);
Console.WriteLine("Size:"+obj.size);
And so on for each property. But instead of it, I was trying to create a code where this 'label' doesn't needed to be hardcoded, so I could print every property dynamically.
I got something like that, using System.Attribute class:
public class MyObject
{
[MyCustomLabel("Color:")]
public string Color;
[MyCustomLabel("Size:")]
public string Size;
//etc...
}
So here comes my problem: retrieving this Attribute's value is not impossible, but it's not friendly since I had to use some reflection for it.
I'm not really scared of using reflection, but it seemed that I was using attributes for something that it wasn't created for.
I wonder where are the best places for using attributes, and if this is really a situation for using it.
Attributes and reflection go hand in hand. With the exception of some compiler/runtime attributes, there's no way to use them with out reflecting over the code.
That said, your approach is reasonable, and you might want to take a look at the attributes in the System.ComponentModel
namespace which have a number of classes to decorate properties with useful metadata.
You're on the right track.
Also, there is a [DisplayName] attribute already tailor-made for this purpose, which has been in .NET since 2.0.
http://msdn.microsoft.com/en-us/library/system.componentmodel.displaynameattribute.aspx
If you're just writing to the console, i.e. it's debugging style output then what you want is minimal chance of typo/copy paste error.
This is confusing under the hood but is highly effective at the call sites:
public static void WriteNameAndValue<T,TValue>(
this TextWriter tw, T t,
Expression<Func<T, TValue>> getter)
{
var memberExpression = getter.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentException("missing body!");
var member = memberExpression.Member;
tw.Write(member.Name);
tw.Write(": ");
if (member is FieldInfo)
{
tw.Write(((FieldInfo)member).GetValue(t));
}
else if (member is PropertyInfo)
{
tw.Write(((PropertyInfo)member).GetValue(t, null));
}
}
public static void WriteNameAndValueLine<T,TValue>(
this TextWriter tw, T t,
Expression<Func<T, TValue>> getter)
{
WriteNameAndValue<T,TValue>(tw, t, getter);
tw.WriteLine();
}
then you can write
t.Foo = "bar";
t.Bar = 32.5;
Console.Out.WriteNameAndValueLine(t, x => x.Foo);
Console.Out.WriteNameAndValueLine(t, x => x.Bar);
// output
// Foo: bar
// Bar: 32.5
If you want this to be more configurable at runtime via resources and with considerations for localization you can do so but I would consider another, more standardized, approach instead if this was a likely requirement.
P.S. if you wanted to get fancy you could replace the FieldInfo/PropertyInfo switch with
tw.Write(getter.Compile()(t));
and then you could check for MethodInfo in the expression as well (or allow arbitrary lambdas and just insert the line number or some other generic text. I suggest not going down this route, the usage is already confusing also this may cause unwanted load in what should be a simple logging method.
this is exactly how Serialization works so I would say your approach is reasonable. Another way you can approach this is to create a dictionary of PropertyNames and their Titles and then look up the title based on the property name.
I have an object to be printed in my application, and I need a label before each property (or just a string before it).
Why do you think of using attributes in your case? Where do you print this objects in your application.
Maybe just implement ToString in your object(s)
E.g.
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("PropertyX: ");
sb.AppendLine(this.PropertyX);
// get string from resource file
sb.Append(Resources.FileName);
sb.Append(": ");
sb.AppendLine(this.FileName);
sb.Append("Number: ");
sb.AppendLine(this.Number.ToString());
return sb.ToString();
}