views:

309

answers:

4

This is a scenario created to help understand what Im trying to achieve.

I am trying to create a method that returns the specified property of a generic object

e.g.

public object getValue<TModel>(TModel item, string propertyName) where TModel : class{
    PropertyInfo p = typeof(TModel).GetProperty(propertyName);
    return p.GetValue(item, null);
}

The code above works fine if you're looking for a property on the TModel item e.g.

string customerName = getValue<Customer>(customer, "name");

However, if you want to find out what the customer's group's name is, it becomes a problem: e.g.

string customerGroupName = getValue<Customer>(customer, "Group.name");

Hoping someone can give me some insight on this way out scenario - thanks.

+3  A: 

I'm guessing you just need to break this down into a couple of steps rather than trying to do it all in one, something like:

// First get the customer group Property...
CustomerGroup customerGroup = getValue<Customer>(customer, "Group");
// Then get the name of the group...
if(customerGroup != null)
{
    string customerGroupName = getValue<CustomerGroup>(customerGroup, "name");
}
xan
A: 

Since Group is the property of the customer, which itself hosts the property name, you have to go this way too. But since '.' cant be part of the name of the property you can easyly use String.Substring to remove the first property name from string and call your method recursively.

Marks
+4  A: 

Here's a simple method that uses recursion to solve your problem. It allows you to traverse an object graph by passing a "dotted" property name. It works with properties as well as fields.

static class PropertyInspector 
{
    public static object GetObjectProperty(object item,string property)
    {
        if (item == null)
            return null;

        int dotIdx = property.IndexOf('.');

        if (dotIdx > 0)
        {
            object obj = GetObjectProperty(item,property.Substring(0,dotIdx));

            return GetObjectProperty(obj,property.Substring(dotIdx+1));
        }

        PropertyInfo propInfo = null;
        Type objectType = item.GetType();

        while (propInfo == null && objectType != null)
        {
            propInfo = objectType.GetProperty(property, 
                      BindingFlags.Public 
                    | BindingFlags.Instance 
                    | BindingFlags.DeclaredOnly);

            objectType = objectType.BaseType;
        }

        if (propInfo != null)
            return propInfo.GetValue(item, null);

        FieldInfo fieldInfo = item.GetType().GetField(property, 
                      BindingFlags.Public | BindingFlags.Instance);

        if (fieldInfo != null)
            return fieldInfo.GetValue(item);

        return null;
    }
}

Example:

class Person
{
   public string Name { get; set; }
   public City City { get; set; }
}

class City
{
   public string Name { get; set; }
   public string ZipCode { get; set; }
}

Person person = GetPerson(id);

Console.WriteLine("Person name = {0}", 
      PropertyInspector.GetObjectProperty(person,"Name"));

Console.WriteLine("Person city = {0}",
      PropertyInspector.GetObjectProperty(person,"City.Name"));
Philippe Leybaert
Legend.........
Jimbo
Thanks alot for this, a great piece to learn from, however I have accepted an answer that is build into .NET
Jimbo
+2  A: 

Hello,

In the System.Web.UI namespace there's a method to do that:

DataBinder.Eval(source, expression);
Guillaume86
UBER Legend...!
Jimbo
Good catch. The only "problem" I have with this one is that it depends on System.Web.dll
Philippe Leybaert
Yeah it should probably go to another assembly
Guillaume86
I was concerned about that - WHY is it in System.Web.UI?
Jimbo
I guess they use it in asp.net templated controls with <%# Eval() %> directives, I'm not very familiar with non-web .net so i don't know if there's an alternative.
Guillaume86