2009-03-10 27 views
8

Sto utilizzando un attributo personalizzato per definire in che modo i membri di una classe vengono mappati alle proprietà per la pubblicazione come post di un modulo (Payment Gateway). Ho l'attributo personalizzato che funziona bene e sono in grado di ottenere l'attributo con "nome", ma vorrei ottenere l'attributo dal membro stesso.Attributi personalizzati per i membri della classe

Ad esempio:

getFieldName("name"); 

vs

getFieldName(obj.Name); 

Il piano è quello di scrivere un metodo per serializzare la classe con membri in una stringa postable.

Ecco il codice di prova che ho a questo punto, dove ret è una stringa e PropertyMapping è l'attributo personalizzato:

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

Grazie in anticipo!

+0

Inoltre, se c'è un approccio migliore sono tutto orecchie :) – ccook

risposta

9

non si può davvero fai questo, a meno che tu non stia usando C# 3.0, nel qual caso dovrai fare affidamento su LINQ (ehm, alberi di espressione).

Quello che si fa è creare un metodo fittizio per un'espressione lambda che consente al compilatore di generare l'albero dell'espressione (il compilatore esegue il controllo del tipo). Quindi scavare in quell'albero per ottenere il membro. In questo modo:

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

Data la seguente classe:

class MyClass 
{ 
    public int a; 
} 

È possibile ottenere i metadati come questo:

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

con un riferimento a quel campo si può quindi scavare qualsiasi attribuisci il solito modo. cioè riflessione.

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

Ora è possibile scavare un attributo su qualsiasi campo da qualcosa che è in grande controllato dal compilatore. Funziona anche con il refactoring, se si rinomina 'a' Visual Studio lo prenderà.

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

Non c'è una sola stringa letterale in quel codice.

+0

Wow, grazie! Funziona sicuramente, l'ho appena testato. Molto bello :) – ccook

+0

Non dimenticare di contrassegnare come risposta;) –

+0

Penso che restituisca membro.Membro come FieldInfo; dovrebbe essere il tipo di ritorno di MemberInfo. Altrimenti il ​​casting restituisce sempre null. – Jeff

4

Si può fare la metà di esso un po 'più semplice:

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

btw; dovresti abituarti a terminare gli attributi con la parola Attribute. Anche se ciò causa la duplicazione (vedere [XmlAttributeAttribute]).

Tuttavia - re serializzazione; non è sempre banale. Una quantità ingannevole del codice va in quadri di serializzazione come Json.NET ecc L'approccio normale potrebbe essere quello di ottenere un tipo di convertitore, ma per molti versi questo è più facile con un PropertyDescriptor:

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

Grazie Marc, ho adottato le due modifiche come suggerito :) – ccook

+0

Sarebbe meglio usare un descrittore di proprietà? – ccook

+0

Dipende da cosa si sta facendo ;-p PropertyDescriptor rende leggermente più semplice ottenere il convertitore * correct *, ecc., Ma per molti aspetti sono simili. Ci sono alcune cose che puoi fare con uno * xor * l'altro, ma molte cose possono essere fatte in entrambi. –

Problemi correlati