【问题标题】:Using System.Attribute class使用 System.Attribute 类
【发布时间】:2009-06-30 03:43:33
【问题描述】:

我遇到了这样一种情况:使用 System.Attribute 类(乍一看)似乎是个好主意。

我有一个要在我的应用程序中打印的对象,我需要在每个属性之前添加一个标签(或者只是在它之前添加一个字符串)。我可以将每个属性硬编码为:

Console.WriteLine("Color:"+obj.color);
Console.WriteLine("Size:"+obj.size);

每个属性以此类推。但不是它,我试图创建一个不需要硬编码这个“标签”的代码,这样我就可以动态地打印每个属性。

我得到了类似的东西,使用 System.Attribute 类:

public class MyObject 
{
    [MyCustomLabel("Color:")]
    public string Color;

    [MyCustomLabel("Size:")]
    public string Size;
    //etc...
}

所以我的问题来了:检索这个属性的值不是不可能的,但它并不友好,因为我不得不使用一些反射。

我并不真的害怕使用反射,但似乎我在使用属性来实现它不是为它创建的东西。

我想知道使用属性的最佳位置在哪里,如果这真的是使用它的情况。

【问题讨论】:

  • 当有人要求您提供法语版本的应用程序时,您会怎么做?将面向用户的“魔术字符串”放入资源中,以便您可以轻松地将它们拉出并本地化,而不是放入将它们“嵌入”到元数据中的属性中。
  • 关于属性与属性的更多想法:blogs.msdn.com/ericlippert/archive/2009/02/02/…
  • 你为什么不喜欢反思?
  • 我不明白 Eric Lippert 的评论。属性也可以使用可本地化的资源:例如继承自 System.ComponentModel.DescriptionAttribute 的内部 System.Windows.Forms.SRDescriptionAttribute 从资源中获取字符串。
  • “但是为什么要经历所有这些繁琐的事情?它能给你带来什么?” - 询问实现它的 .NET 团队。在他们的情况下,我怀疑这主要是因为 VS 属性窗口中的智能感知可以显示本地化描述。

标签: c# reflection code-reuse


【解决方案1】:

属性和反射齐头并进。除了一些编译器/运行时属性之外,如果不反映代码,就无法使用它们。

也就是说,您的方法是合理的,您可能想看看 System.ComponentModel 命名空间中的属性,这些属性有许多用有用的元数据装饰属性的类。

【讨论】:

  • 嗯,一组有趣的课程!我正在研究它。谢谢 =)
【解决方案2】:

你在正确的轨道上。

此外,已经为此目的定制了一个 [DisplayName] 属性,该属性自 .NET 2.0 起就已存在。

http://msdn.microsoft.com/en-us/library/system.componentmodel.displaynameattribute.aspx

【讨论】:

    【解决方案3】:

    如果您只是在向控制台写入数据,即它是调试样式输出,那么您想要的就是将出现拼写错误/复制粘贴错误的可能性降到最低。

    这在后台令人困惑,但在调用站点非常有效:

    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();
    }
    

    那你就可以写了

    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
    

    如果您希望它在运行时通过资源更加可配置,并且考虑到本地化,您可以这样做,但如果这是可能的要求,我会考虑另一种更标准化的方法。

    附:如果您想变得花哨,可以将 FieldInfo/PropertyInfo 开关替换为

    tw.Write(getter.Compile()(t));
    

    然后您也可以在表达式中检查 MethodInfo (或允许任意 lambdas 并仅插入行号或其他一些通用文本。我建议不要沿着这条路线走,用法已经令人困惑,这也可能导致不需要的加载应该是一个简单的日志记录方法。

    【讨论】:

      【解决方案4】:

      这正是序列化的工作方式,所以我会说你的方法是合理的。解决此问题的另一种方法是创建一个 PropertyNames 及其标题的字典,然后根据属性名称查找标题。

      【讨论】:

        【解决方案5】:

        我有一个要在我的应用程序中打印的对象,我需要在每个属性之前添加一个标签(或者只是在它之前的一个字符串)。

        您为什么会考虑在您的案例中使用属性?您在应用程序中的何处打印此对象。

        也许只是在您的对象中实现 ToString
        例如

        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();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-07-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-12
          • 2022-10-05
          相关资源
          最近更新 更多