【问题标题】:Custom Attributes on Class Members类成员的自定义属性
【发布时间】:2010-10-12 10:26:23
【问题描述】:

我正在使用自定义属性来定义一个类的成员如何映射到属性以作为表单发布(支付网关)发布。我的自定义属性工作得很好,并且能够通过“名称”获取属性,但想通过成员本身获取属性。

例如:

getFieldName("name");

getFieldName(obj.Name);

计划是编写一个方法,将具有成员的类序列化为可发布的字符串。

这是我此时的测试代码,其中 ret 是一个字符串,PropertyMapping 是自定义属性:

foreach (MemberInfo i in (typeof(CustomClass)).GetMember("Name"))
{
    foreach (object at in i.GetCustomAttributes(true))
    {
        PropertyMapping map = at as PropertyMapping;
        if (map != null)
        {
            ret += map.FieldName;
        }
    }
}

提前致谢!

【问题讨论】:

  • 另外,如果有更好的方法,我会全力以赴:)

标签: c# reflection attributes custom-attributes


【解决方案1】:

您不能真正做到这一点,除非您使用的是 C# 3.0,在这种情况下您需要依赖 LINQ(嗯,表达式树)。

您所做的是为 lambda 表达式创建一个虚拟方法,让编译器生成表达式树(编译器进行类型检查)。然后你深入那棵树来获取成员。像这样:

static FieldInfo GetField<TType, TMemberType>(
    Expression<Func<TType, TMemberType>> accessor)
{
    var member = accessor.Body as MemberExpression;
    if (member != null)
    {
        return member.Member as FieldInfo;
    }
    return null; // or throw exception...
}

给定以下类:

class MyClass
{
    public int a;
}

你可以这样获取元数据:

// get FieldInfo of member 'a' in class 'MyClass'
var f = GetField((MyClass c) => c.a); 

通过对该字段的引用,您可以按照通常的方式挖掘任何属性。即反射。

static TAttribute GetAttribute<TAttribute>( 
    this MemberInfo member ) where TAttribute: Attribute
{
    return member.GetCustomAttributes( typeof( TAttribute ), false )
        .Cast<TAttribute>().FirstOrDefault<TAttribute>();
}

现在你可以通过编译器检查的东西来挖掘任何字段的属性。它也适用于重构,如果您重命名“a”,Visual Studio 会捕捉到这一点。

var attr = GetField((MyClass c) => c.a).GetAttribute<DisplayNameAttribute>();
Console.WriteLine(attr.DisplayName);

那里的代码中没有一个文字字符串。

【讨论】:

  • 哇,谢谢!这绝对有效,我刚刚测试了它。非常好:)
  • 我认为返回 member.Member 作为 FieldInfo;应该是 MemberInfo 的返回类型。否则,转换总是返回 null。
  • @Jeffery C,是的,当然,这个方法称为 GetField,就语义而言,我认为这很好。但是你可以制作一个 GetMember 来做到这一点。它会更通用。但是,虽然 PropertyInfo 和 FieldInfo 导致 MemberAccess 节点类型,但 MethodInfo 派生(也从 MemberInfo 派生)会生成不同的表达式树,如果您这样做,则必须处理该方法主体,以免弄乱符号。
【解决方案2】:

你可以更简单地做一半:

    foreach (PropertyMapping attrib in
        Attribute.GetCustomAttributes(i, typeof(PropertyMapping)))
    {
        ret += map.FieldName; // whatever you want this to do...
    }

顺便说一句;你应该养成使用Attribute 来结束属性的习惯。即使这会导致重复(参见[XmlAttributeAttribute])。

然而——重新序列化;这并不总是微不足道的。有大量欺骗性的代码进入 Json.NET 等序列化框架。通常的方法可能是获取类型转换器,但在许多方面,使用PropertyDescriptor 更容易:

    foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(obj))
    {
        Console.WriteLine("{0}={1}",
            prop.Name, prop.Converter.ConvertToInvariantString(
                prop.GetValue(obj)));
    }

【讨论】:

  • 谢谢你,Marc,我已按照建议采纳了你的两项更改:)
  • 使用属性描述符会更好吗?
  • 这取决于您在做什么;-p PropertyDescriptor 使获得 正确 转换器等变得稍微容易一些 - 但在大多数方面它们是相似的。你可以用一个xor做一些事情,但大多数事情都可以用任何一个来完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-25
  • 2013-07-27
  • 2012-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-07
相关资源
最近更新 更多