2009-03-10 2 views
8

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

Например:

getFieldName("name"); 

против

getFieldName(obj.Name); 

План состоит в том, чтобы написать метод сериализации класса с членами в postable строку.

Вот тестовый код у меня есть на данный момент, где 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; 
     } 
    } 
} 

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

+0

Кроме того, если есть лучший подход, я все уши :) – ccook

ответ

9

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

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

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

В этом коде нет ни одной строки литерала.

+0

Вау, спасибо! Это определенно работает, я только что проверил его. Очень приятно :) – ccook

+0

Не забудьте отметить как ответ;) –

+0

Я думаю, что return member.Member как FieldInfo; должен быть возвращаемым типом MemberInfo. В противном случае кастинг всегда возвращает null. – Jeff

4

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

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))); 
    } 
+0

Спасибо, Марк, я принял ваши два изменения, как было предложено :) – ccook

+0

Было бы лучше использовать дескриптор свойства? – ccook

+0

Это зависит от того, что вы делаете: -p PropertyDescriptor упрощает получение * правильного * конвертера и т. Д., Но в большинстве случаев они похожи. Есть несколько вещей, которые вы можете сделать с одним * xor * другим, но большинство вещей можно сделать в любом случае. –

Смежные вопросы