Пользовательские атрибуты для членов класса - PullRequest
8 голосов
/ 10 марта 2009

Я использую пользовательский атрибут, чтобы определить, как члены класса сопоставляются со свойствами для публикации в форме публикации (Платежный шлюз). У меня есть настраиваемый атрибут, который работает нормально, и я могу получить атрибут по «имени», но хотел бы получить атрибут самим членом.

Например:

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

Заранее спасибо!

Ответы [ 2 ]

9 голосов
/ 10 марта 2009

На самом деле вы не можете сделать это, если только вы не используете C # 3.0, в этом случае вам нужно полагаться на LINQ (эм, деревья выражений).

Что вы делаете, так это то, что вы создаете фиктивный метод для лямбда-выражения, который позволяет компилятору генерировать дерево выражений (компилятор выполняет проверку типов). Затем вы копаетесь в этом дереве, чтобы получить член. Вот так:

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

Там нет ни одной литеральной строки в этом коде.

4 голосов
/ 10 марта 2009

Вы можете сделать половину из этого немного проще:

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