views:

600

answers:

2

I am using a Custom Attribute to define how a class's members are mapped to properties for posting as a form post (Payment Gateway). I have the custom attribute working just fine, and am able to get the attribute by "name", but would like to get the attribute by the member itself.

For example:

getFieldName("name");

vs

getFieldName(obj.Name);

The plan is to write a method to serialize the class with members into a postable string.

Here's the test code I have at this point, where ret is a string and PropertyMapping is the custom attribute:

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

Thanks in advance!

+2  A: 

You can do half of it a bit more simply:

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

btw; you should make a habit of ending attributes with the word Attribute. Even if this causes duplication (see [XmlAttributeAttribute]).

However - re serialization; that isn't always trivial. A deceptive amount of code goes into serialization frameworks like Json.NET etc. The normal approach might be to get a type-converter, but in many ways this is easier with a PropertyDescriptor:

    foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(obj))
    {
        Console.WriteLine("{0}={1}",
            prop.Name, prop.Converter.ConvertToInvariantString(
                prop.GetValue(obj)));
    }
Marc Gravell
Thank you Marc, I have adopted your two changes as suggested :)
ccook
Would it be better to use a property descriptor?
ccook
It depends on what you are doing ;-p PropertyDescriptor makes it slightly easier to obtain the *correct* converter, etc - but in most respects they are similar. There are some things you can do with one *xor* the other, but most things can be done in either.
Marc Gravell
Nicely put :) ty
ccook
+6  A: 

You can't really do this, unless you're using C# 3.0 in which case you'll need to rely on LINQ (ehm, expression trees).

What you do is that you create a dummy method for a lambda expression which lets the compiler generate the expression tree (compiler does the type checking). Then you dig into that tree to get the member. Like so:

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

Given the following class:

class MyClass
{
    public int a;
}

You can get the meta data like this:

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

With a reference to that field you can then dig up any attribute the usual way. i.e. reflection.

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

Now you can dig up an attribute on any field by something which is in large checked by the compiler. It also works with reflection, if you rename 'a' Visual Studio will catch that.

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

There's not a single literal string in that code there.

John Leidegren
Wow, thank you! This definitely works, I just tested it out. Very nice :)
ccook
Don't forget to mark as answer ;)
John Leidegren
@John, I think return member.Member as FieldInfo; should be return type of MemberInfo. Otherwise the casting always returns null.
Jeffrey C
@Jeffery C, yeah, definitely, this method is called GetField, in terms of semantics I think that's fine. But you could make a GetMember which does exactly that. It will be more generic. However, while PropertyInfo and FieldInfo result in a MemberAccess node type the MethodInfo derive which also derives from MemberInfo generates a different expression tree and you have to deal with that method body if you do it like that to not mess up the scemantic.
John Leidegren