views:

54

answers:

2

Let's say I have a class that looks like this:

public class CallByAttribute
{
    [CodeName("Foo")]
    public string MyProperty { get; set; }

    [CodeName("Bar")]
    public string MyMethod(int someParameter)
    {
         return myDictionary[someParameter];
    }
}

How would I call these two properties or methods, using CodeName instead of the property or method name?

+3  A: 

Method 1:

public static TOutput GetPropertyByCodeName<TOutput>(this object obj, string codeName)
{
    var property = obj.GetType()
                      .GetProperties()
                      .Where(p => p.IsDefined(typeof(CodeNameAttribute), false))
                      .Single(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false).First())).Name == codeName);

    return (TOutput)property.GetValue(obj, null);
}

Note: This will throw if no property exists with the specified codeName or if multiple properties share the same codeName.

Usage:

CallByAttribute obj= ...
string myProperty = obj.GetPropertyByCodeName<string>("Foo");

Method 2:

If you are on C# 4, you can write your own System.Dynamic.DynamicObject that can route dynamic calls to the right member.

This will allow for cleaner syntax. For example, you should be able to accomplish something that allows:

CallByAttribute obj= ...
dynamic objectByCodeName = new ObjectByCodeName(obj);
objectByCodeName.Foo = "8";
objectByCodeName.Bar();
Ani
It looks like you need a companion `Attribute` class for `CodeName`; is that true, or can you just reflect over the properties to get the CodeName for each?
Robert Harvey
Robert: Presumably you already have a `CodeNameAttribute` class, otherwise you wouldn't be able to do `[CodeName("foo")]` because that actually causes an instantiation of a `CodeNameAttribute` object.
Gabe
If you're going to be using these codenames a lot (thousands or millions of times), you'll want to cache the results of the property lookup as delegates that you can lookup in a dictionary.
Gabe
@Gabe: Thanks for the tip!
Robert Harvey
+1  A: 

Here's some fully working code including optional call arguments:

    private static string Call(object callByAttribute, string name, object[] args)
    {
        PropertyInfo prop = callByAttribute.GetType().GetProperties()
            .Where(p => p.IsDefined(typeof(CodeNameAttribute), false))
             .SingleOrDefault(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false)).First()).Name == name);
        if (prop != null)
            return (string)callByAttribute.GetType().InvokeMember(prop.Name, BindingFlags.GetProperty, null, callByAttribute, null);


        MethodInfo method = callByAttribute.GetType().GetMethods()
            .Where(p => p.IsDefined(typeof(CodeNameAttribute), false))
             .SingleOrDefault(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false)).First()).Name == name);
        if (method != null)
            return (string)callByAttribute.GetType().InvokeMember(method.Name,  BindingFlags.InvokeMethod, null, callByAttribute, args);

        throw new Exception("method/getter not found");
    }
    private static string Call(object callByAttribute, string name)
    {
        return Call(callByAttribute, name, null);
    }

This can be used in a complete program like this:

using System;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
public class CallByAttribute
{
    [CodeName("Foo")]
    public string MyProperty { get; set; }

    [CodeName("Bar")]
    public string MyMethod(int someParameter)
    {
        return "blah" + someParameter;
    }
}

public class CodeNameAttribute : Attribute
{
    private readonly string name;

    public CodeNameAttribute(string name)
    {
        this.name = name;
    }

    public string Name
    {
        get { return name; }
    }
}


class Program
{
    static void Main(string[] args)
    {
        CallByAttribute callByAttribute = new CallByAttribute();
        callByAttribute.MyProperty = "hi";

        Console.WriteLine(Call(callByAttribute, "Bar", new object[] {1}));
        Console.WriteLine(Call(callByAttribute, "Foo"));

    }
    private static string Call(object callByAttribute, string name)
    {
        return Call(callByAttribute, name, null);
    }


    private static string Call(object callByAttribute, string name, object[] args)
    {
        PropertyInfo prop = callByAttribute.GetType().GetProperties()
            .Where(p => p.IsDefined(typeof(CodeNameAttribute), false))
             .SingleOrDefault(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false)).First()).Name == name);
        if (prop != null)
            return (string)callByAttribute.GetType().InvokeMember(prop.Name, BindingFlags.GetProperty, null, callByAttribute, null);

        MethodInfo method = callByAttribute.GetType().GetMethods()
            .Where(p => p.IsDefined(typeof(CodeNameAttribute), false))
             .SingleOrDefault(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false)).First()).Name == name);
        if (method != null)
            return (string)callByAttribute.GetType().InvokeMember(method.Name,  BindingFlags.InvokeMethod, null, callByAttribute, args);

        throw new Exception("method/getter not found");
    }
}
}
Philipp
Thanks, Philip.
Robert Harvey