Reputation: 8903
I want 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;
}
}
This works fine, but I may wind up with a rather large list... So I was curious if there's 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<string, something>
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.
I'm specifically trying to avoid reflection etc in order to have a very fast implementation. I'll likely use code generation, so the solution doesn't 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, I'm afraid the continuous 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.
Upvotes: 6
Views: 1362
Reputation: 48959
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.
Upvotes: 5
Reputation: 59513
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;
}
Upvotes: 7
Reputation: 78282
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);
}
}
Upvotes: 0
Reputation: 4948
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);
}
}
Upvotes: 2
Reputation: 245479
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).
Upvotes: 8
Reputation: 103545
Well, assuming that the Name matches the actual name of the property (unlike your example), this would probably be best handled through reflection.
Upvotes: 0