Atributos personalizados em Membros da Classe
-
08-07-2019 - |
Pergunta
Eu estou usando um atributo personalizado para definir como membros de uma classe são mapeadas para propriedades para postar como um post de formulário (Gateway de Pagamento). Eu tenho o atributo personalizado trabalhando muito bem, e sou capaz de conseguir o atributo pelo "nome", mas gostaria de obter o atributo pelo membro em si.
Por exemplo:
getFieldName("name");
vs
getFieldName(obj.Name);
O plano é escrever um método para serializar a classe com membros em uma string postable.
Aqui está o código de teste que eu tenho neste momento, em que ret é uma cadeia e PropertyMapping é o atributo personalizado:
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;
}
}
}
Agradecemos antecipadamente!
Solução
Você não pode realmente fazer isso, a menos que você estiver usando C # 3.0, caso em que você vai precisar contar com LINQ (ehm, árvores de expressão).
O que você faz é que você criar um método dummy para uma expressão lambda que permite que o compilador gerar a árvore de expressão (compilador faz a verificação de tipo). Então você cavar em que a árvore para obter o membro. Como assim:
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...
}
Dada a seguinte classe:
class MyClass
{
public int a;
}
Você pode obter os dados de meta como esta:
// get FieldInfo of member 'a' in class 'MyClass'
var f = GetField((MyClass c) => c.a);
Com uma referência a esse campo, você pode então desenterrar qualquer atributo da maneira usual. ou seja reflexão.
static TAttribute GetAttribute<TAttribute>(
this MemberInfo member ) where TAttribute: Attribute
{
return member.GetCustomAttributes( typeof( TAttribute ), false )
.Cast<TAttribute>().FirstOrDefault<TAttribute>();
}
Agora você pode cavar-se um atributo em qualquer campo por algo que é em grande verificada pelo compilador. Ele também funciona com refatoração, se mudar o nome 'a' Visual Studio vai pegar isso.
var attr = GetField((MyClass c) => c.a).GetAttribute<DisplayNameAttribute>();
Console.WriteLine(attr.DisplayName);
Não há uma única string literal nesse código lá.
Outras dicas
Você pode fazer metade do que um pouco mais simples:
foreach (PropertyMapping attrib in
Attribute.GetCustomAttributes(i, typeof(PropertyMapping)))
{
ret += map.FieldName; // whatever you want this to do...
}
btw; você deve fazer um hábito de acabar com atributos com a palavra Attribute
. Mesmo se isso faz com que a duplicação (veja [XmlAttributeAttribute]
).
No entanto - re serialização; que nem sempre é trivial. Uma quantidade enganosa de código entra em estruturas de serialização como Json.NET etc. A abordagem normal, poderia ser a de obter um conversor tipo, mas em muitos aspectos, isso é mais fácil com um PropertyDescriptor
:
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(obj))
{
Console.WriteLine("{0}={1}",
prop.Name, prop.Converter.ConvertToInvariantString(
prop.GetValue(obj)));
}