Using System.Attribute class

2019-08-20 07:51发布

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.

5条回答
ゆ 、 Hurt°
2楼-- · 2019-08-20 08:10

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();
}
查看更多
ゆ 、 Hurt°
3楼-- · 2019-08-20 08:11

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.

查看更多
够拽才男人
4楼-- · 2019-08-20 08:23

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

查看更多
冷血范
5楼-- · 2019-08-20 08:24

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.

查看更多
ゆ 、 Hurt°
6楼-- · 2019-08-20 08:30

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.

查看更多
登录 后发表回答