views:

196

answers:

7

(added updates below) I'm wanting to build a method which accepts a string param, and an object which I would like to return a particular member of based on the param. So, the easiest method is to build a switch statement:

public GetMemberByName(MyObject myobj, string name)
{
   switch(name){
     case "PropOne": return myobj.prop1;
     case "PropTwo": return myobj.prop2; 
   }
}

Works fine, but I may wind up with a rather large list.. so I was curious if theres a way, without writing a bunch of nested if-else structures, to accomplish this in an indexed way, so that the matching field is found by index instead of falling through a switch until a match is found.

I considered using a Dictionary to give fast access to the matching strings (as the key member) but since I'm wanting to access a member of a passed-in object, I'm not sure how this could be accomplished.

(updates)
Ok looks like I need a few more specifics for what I'm trying to do

-Im specifically trying to avoid reflection etc in order to have a very fast implementation. I'll likley use code generation, so the solution doesnt need to be small/tight etc.

-I originally was building a dictionary of but each object was initializing it. So I began to move this to a single method that can look up the values based on the keys- a switch statement. But since I'm no longer indexed, Im afraid the contniuous lookups calling this method would be slow.

-SO: I am looking for a way to combine the performance of an indexed/hashed lookup (like the Dictionary uses) with returning particular properties of a passed-in object. I'll likely put this in a static method within each class it is used for.

Thanks for all the answers so far.

+2  A: 

You can use reflection to get a property dynamically at runtime. Here is a snippet from a little relection utility i wrote. This is written as an extension method which would easily allow you to get a property from your class instance

myInstance.GetProperty<string>("Title"); // Replace 'string' with the proper return value.

The code:

public static class ReflectionExtensions
{
    private const BindingFlags DefaultFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;

    public static T GetProperty<T>(this object instance, string propertyName)
    {
        PropertyInfo property = GetPropertyInfo(instance, propertyName);
        if (property == null)
        {
            var message = string.Format("The Type, '{0}' does not implement a '{1}' property", instance.GetType().AssemblyQualifiedName, propertyName);
            throw new NotImplementedException(message);
        }

        return (T)property.GetValue(instance, null);
    }

    private static PropertyInfo GetPropertyInfo(object instance, string propertyName)
    {
        Type type = instance.GetType();
        return type.GetProperty(propertyName, DefaultFlags);
    }
}
Wallace Breza
+1, excellent suggestion to use a generic extension method.
John M Gant
One downside, though (see my comment on Justin Niessner's post) is that if the return type is generic, you have to specify it in the method call, because the compiler can't infer the type of T using the return value alone. So your method call would actually be MyInstance.GetProperty<string>("Title"), or whatever type the property was.
John M Gant
@John M Gant, you are correct, i've updated my sample.
Wallace Breza
Any generic implementation is going to have a problem in practice. If program actually contains `myInstance.GetProperty<string>("Title")`, then it might as well have `myInstance.Title`. If you're passing in the field in a variable, then how do you know it's a `string`?
Jeffrey L Whitledge
@Jeffrey L Whitledge, If you have the correct type of instance in the first place then there is no reason for this exercise when they can call the property the standard way. Typically in this type of scenerio you don't have the type information at runtime, but you know you need to access a property called "x". I've also used this utility against anonymous types as well since there is not way to cast to an anonymous type.
Wallace Breza
A: 

Well, assuming that the Name matches the actual name of the property (unlike your example), this would probably be best handled through reflection.

James Curran
A: 

You cant do it with an index, but you could use reflection.

Ben Robinson
+8  A: 

Here's a quick mockup of something that could work for any class (using reflection rather than a switch statement):

public static object GetMemberByName<T>(T obj, string name)
{
    PropertyInfo prop = typeof(T).GetProperty(name);
    if(prop != null)
        return prop.GetValue(obj, null);

    throw new ArgumentException("Named property doesn't exist.");
}

Or an Extension Method version (which will still work on any object type):

public static object GetMemberByName<T>(this T obj, string name)
{
    PropertyInfo prop = typeof(T).GetProperty(name);
    if(prop != null)
        return prop.GetValue(obj, null);

    throw new ArgumentException("Named property doesn't exist.");
}

Obviously there's some additional error checking you might want to do, but this would be a start.

I also returned the type object from the methods for a reason. This allows the caller to handle casting the value however they see fit (if they need to cast at all).

Justin Niessner
+1 Was going to answer the same way so gave you the point.
Ioxp
Interestingly, Wallace Breza's version accepts an object and returns a T, while this version accepts a T and returns an object. You could also have a method that's generic as to both input and output.
John M Gant
@John M Gant: I chose to make it generic on the return object so there isn't any casting involved when using the return value. I also wanted the extension method to show up in intellisence for all objects.
Wallace Breza
@Wallace, it's still available on all objects either way.
John M Gant
Wallace Breza
@John M Gant: @Justin Niessner is using typeof(T) whereas I'm using instance.GetType(). I don't think it makes a difference either way.
Wallace Breza
@Wallace, you're right it doesn't make a difference whether you use typeof or GetType. Your edit fixed the other problem. Again, excellent suggestion.
John M Gant
My mistake for not specifying, I'm trying to avoid reflection. Great answer, but not what I'm trying to do. I added some clarification above.
boomhauer
A: 

You may want to try using something like this.

private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _cache = new Dictionary<Type,Dictionary<string,PropertyInfo>>();
public static T GetProperty<T>(object obj, string name)
{
    if (obj == null)
    {
        throw new ArgumentNullException("obj");
    }
    else if (name == null)
    {
        throw new ArgumentNullException("name");
    }

    lock (_cache)
    {
        var type = obj.GetType();
        var props = default(Dictionary<string, PropertyInfo>);
        if (!_cache.TryGetValue(type, out props))
        {
            props = new Dictionary<string, PropertyInfo>();
            _cache.Add(type, props);
        }
        var prop = default(PropertyInfo);
        if (!props.TryGetValue(name, out prop))
        {
            prop = type.GetProperty(name);
            if (prop == null)
            {
                throw new MissingMemberException(name);
            }
            props.Add(name, prop);
        }
        return (T)prop.GetValue(obj, null);
    }
}
ChaosPandion
+6  A: 

Here's an easy way you can use a dictionary:

    Dictionary<string, Func<MyObject, object>> propertyNameAssociations;

    private void BuildPropertyNameAssociations()
    {
        propertyNameAssociations = new Dictionary<string, Func<MyObject, object>>();
        propertyNameAssociations.Add("PropOne", x => x.prop1);
        propertyNameAssociations.Add("PropTwo", x => x.prop2);
    }

    public object GetMemberByName(MyObject myobj, string name)
    {
        if (propertyNameAssociations.Contains(name))
            return propertyNameAssociations[name](myobj);
        else
            return null;
    }
Jeffrey L Whitledge
Not bad, you could even use reflection to generate the code. The only caveat being the lack of generic use.
ChaosPandion
@ChaosPandion - Yeah, I am hoping that the return type will be something specific, but that was sadly omitted from the question. Maybe the OP can replace "object" with something better!
Jeffrey L Whitledge
Jeffrey, just what I was looking for, thank you sir.
boomhauer
@boomhauer - You're welcome!
Jeffrey L Whitledge
+4  A: 

There are a few options you can try.

Option 1: Have the object store the property values dynamically.

public GetMemberByName(MyObject myobj, string name)  
{  
  return myobj.GetProperty(name);
}

public class MyObject
{
    private Dictionary<string, object> m_Properties = new Dictionary<string, object>();

    public object GetProperty(string name)
    {
        return m_Properties[name];
    }

    public void SetProperty(string name, object value)
    {
        m_Properties[name] = value;
    }

    public object Prop1
    {
        get { return GetProperty("PropOne"); }
        set { SetProperty("PropOne", value); }
    }

    public object Prop2
    {
        get { return GetProperty("PropTwo"); }
        set { SetProperty("PropTwo", value); }
    }
}

Option 2: Use reflection.

public GetMemberByName(MyObject myobj, string name)  
{  
    return typeof(MyObject).GetProperty(name).GetValue(obj, null);
}

Option 3: Leave it the way it is.

This is a reasonable option because switch statements on string data types will be converted to a Dictionary lookup once the number case statements reaches a certain threshold. That threshold is 7 on the C# 3.0 compiler. So the lookup will be O(1) no matter how many case statements there are. It will not scan through each one.

Brian Gideon