Using reflection in C # to get the properties of a nested object

Given the following objects:

public class Customer { public String Name { get; set; } public String Address { get; set; } } public class Invoice { public String ID { get; set; } public DateTime Date { get; set; } public Customer BillTo { get; set; } } 

I would like to use reflection to go through Invoice to get the Name property of the Customer object. Here is what I need, assuming this code will work:

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo.Address"); Object val = info.GetValue(inv, null); 

Of course, this does not work, because "BillTo.Address" is not a valid property of the Invoice class.

So, I tried to write a method to divide the string into parts by period, and go around objects that look for the final value that interests me. This works fine, but it’s not very convenient for me:

 public Object GetPropValue(String name, Object obj) { foreach (String part in name.Split('.')) { if (obj == null) { return null; } Type type = obj.GetType(); PropertyInfo info = type.GetProperty(part); if (info == null) { return null; } obj = info.GetValue(obj, null); } return obj; } 

Any ideas on how to improve this method, or the best way to solve this problem?

EDIT after posting, I saw several related posts ... However, there seems to be no answer that specifically addresses this issue. Also, I still like the feedback on my implementation.

+67
reflection c #
Dec 23 '09 at 19:10
source share
12 answers

I really think your logic is fine. Personally, I would probably modify it to pass the object as the first parameter (which is more related to PropertyInfo.GetValue, therefore less unexpected).

I would also probably call it something more like GetNestedPropertyValue, so that it is obvious what it is looking for in the property stack.

+11
Dec 23 '09 at 19:19
source share

I use this method to get values ​​from properties such as

"Property"

"Address.Street"

"Address.Country.Name"

  public static object GetPropertyValue(object src, string propName) { if (src == null) throw new ArgumentException("Value cannot be null.", "src"); if (propName == null) throw new ArgumentException("Value cannot be null.", "propName"); if(propName.Contains("."))//complex type nested { var temp = propName.Split(new char[] { '.' }, 2); return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]); } else { var prop = src.GetType().GetProperty(propName); return prop != null ? prop.GetValue(src, null) : null; } } 

Here's the script: https://dotnetfiddle.net/PvKRH0

+13
Apr 23 '15 at 12:26
source share

You need to access the ACTUAL object that you need to reflect. Here is what I mean:

Instead of this:

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo.Address"); Object val = info.GetValue(inv, null); 

Do it (edited based on comment):

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo"); Customer cust = (Customer)info.GetValue(inv, null); PropertyInfo info2 = cust.GetType().GetProperty("Address"); Object val = info2.GetValue(cust, null); 

Look at this post for more information: Using reflection to set the property of an object property

+10
Dec 23 '09 at 19:14
source share

I know I'm a little late to the party, and as others have said, your implementation is fine
... for simple use cases .
However, I developed a library that solves just this use case, Pather.CSharp .
It is also available as a Nuget package .

Its main class is Resolver with its Resolve method.
You pass it an object and a property path and it will return the desired value .

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Address"); 



But it can also allow more complex property paths , including access to an array and a dictionary.
So, for example, if your Customer had several addresses

 public class Customer { public String Name { get; set; } public IEnumerable<String> Addresses { get; set; } } 

you can access the second one using Addresses[1] .

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Addresses[1]"); 
+10
Jan 17 '16 at 19:32
source share

In the hope of not being late for the party, I would like to add my decision: Definitely use recursion in this situation

 public static Object GetPropValue(String name, object obj, Type type) { var parts = name.Split('.').ToList(); var currentPart = parts[0]; PropertyInfo info = type.GetProperty(currentPart); if (info == null) { return null; } if (name.IndexOf(".") > -1) { parts.Remove(currentPart); return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType); } else { return info.GetValue(obj, null).ToString(); } } 
+7
Nov 09
source share

You do not explain the source of your "discomfort", but your code basically looks to me.

The only thing I would ask is error handling. You return null if the code tries to pass through a null reference or if the property name does not exist. This hides the errors: it is hard to know if it returned null because there is no BillTo client, or because you typed it "BilTo.Address" ... or because there is a BillTo client, and its address is invalid! I would let the method crash and write in these cases - just allow the exception (or maybe wrap it in a friendlier one).

+6
Dec 23 '09 at 19:20
source share
 > Get Nest properties eg, Developer.Project.Name 
 private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName) { if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0) throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString())); if (PropertName.Split('.').Length == 1) return t.GetType().GetProperty(PropertName); else return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]); } 
+2
Nov 18 '13 at 17:17
source share
  if (info == null) { /* throw exception instead*/ } 

I would throw an exception if they request a property that does not exist. The way you encoded it, if I call GetPropValue and it returns null, I do not know if this means that the property does not exist, or the property exists, but it is null.

+1
Dec 23 '09 at 19:21
source share
  public static string GetObjectPropertyValue(object obj, string propertyName) { bool propertyHasDot = propertyName.IndexOf(".") > -1; string firstPartBeforeDot; string nextParts = ""; if (!propertyHasDot) firstPartBeforeDot = propertyName.ToLower(); else { firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower(); nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1); } foreach (var property in obj.GetType().GetProperties()) if (property.Name.ToLower() == firstPartBeforeDot) if (!propertyHasDot) if (property.GetValue(obj, null) != null) return property.GetValue(obj, null).ToString(); else return DefaultValue(property.GetValue(obj, null), propertyName).ToString(); else return GetObjectPropertyValue(property.GetValue(obj, null), nextParts); throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'"); } 
+1
Jun 24 '13 at 11:58 on
source share

Here is another implementation that will skip the nested property if it is an enumerator and continues deeper. Validating properties of a type variable does not affect enum validation.

 public static class ReflectionMethods { public static bool IsNonStringEnumerable(this PropertyInfo pi) { return pi != null && pi.PropertyType.IsNonStringEnumerable(); } public static bool IsNonStringEnumerable(this object instance) { return instance != null && instance.GetType().IsNonStringEnumerable(); } public static bool IsNonStringEnumerable(this Type type) { if (type == null || type == typeof(string)) return false; return typeof(IEnumerable).IsAssignableFrom(type); } public static Object GetPropValue(String name, Object obj) { foreach (String part in name.Split('.')) { if (obj == null) { return null; } if (obj.IsNonStringEnumerable()) { var toEnumerable = (IEnumerable)obj; var iterator = toEnumerable.GetEnumerator(); if (!iterator.MoveNext()) { return null; } obj = iterator.Current; } Type type = obj.GetType(); PropertyInfo info = type.GetProperty(part); if (info == null) { return null; } obj = info.GetValue(obj, null); } return obj; } } 

based on this question and on

How to find out if PropertyInfo is a collection of Berryl

I use this in an MVC project to dynamically organize my data by simply passing a property to sort by Example:

 result = result.OrderBy((s) => { return ReflectionMethods.GetPropValue("BookingItems.EventId", s); }).ToList(); 

where BookingItems is a list of objects.

+1
Jun 22 '16 at 13:21
source share

My internet connection was disconnected when I needed to solve the same problem, so I had to "reinvent the wheel":

 static object GetPropertyValue(Object fromObject, string propertyName) { Type objectType = fromObject.GetType(); PropertyInfo propInfo = objectType.GetProperty(propertyName); if (propInfo == null && propertyName.Contains('.')) { string firstProp = propertyName.Substring(0, propertyName.IndexOf('.')); propInfo = objectType.GetProperty(firstProp); if (propInfo == null)//property name is invalid { throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString())); } return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1)); } else { return propInfo.GetValue(fromObject, null); } } 

Quite precisely, this solves the problem for any string that you use for the property name, regardless of the degree of nesting, if the whole property.

0
Jun 21 '12 at 21:24
source share

Try inv.GetType().GetProperty("BillTo+Address");

-7
Dec 23 '09 at 19:30
source share



All Articles