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'd like to use reflection to go through the Invoice to get the Name property of a Customer. Here's what I'm after, assuming this code would 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 fails since "BillTo.Address" is not a valid property of the Invoice class.
So, I tried writing a method to split the string into pieces on the period, and walk the objects looking for the final value I was interested in. It works okay, but I'm not entirely comfortable with it:
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 a better way to solve this problem?
EDIT after posting, I saw a few related posts... There doesn't seem to be an answer that specifically addresses this question, however. Also, I'd still like the feedback on my implementation.
I use following method to get the values from (nested classes) properties like
"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 is the Fiddle: https://dotnetfiddle.net/PvKRH0
I know I'm a bit late to the party, and as others said, your implementation is fine
...for simple use cases.
However, I've developed a library that solves exactly that use case, Pather.CSharp.
It is also available as Nuget Package.
Its main class is Resolver with its Resolve method.
You pass it an object and the 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 resolve more complex property paths, including array and dictionary access.
So, for example, if your Customer had multiple addresses
public class Customer {
public String Name { get; set; }
public IEnumerable<String> Addresses { get; set; }
}
you could 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]");
I actually think your logic is fine. Personally, I would probably change it around so you pass the object as the first parameter (which is more inline with PropertyInfo.GetValue, so less surprising).
I also would probably call it something more like GetNestedPropertyValue, to make it obvious that it searches down the property stack.
You have to access the ACTUAL object that you need to use reflection on. 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 this (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 a property of a property of an object
In hopes of not sounding too late to the party, I would like to add my solution:
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();
}
}
You don't explain the source of your "discomfort," but your code basically looks sound to me.
The only thing I'd question is the error handling. You return null if the code tries to traverse through a null reference or if the property name doesn't exist. This hides errors: it's hard to know whether it returned null because there's no BillTo customer, or because you misspelled it "BilTo.Address"... or because there is a BillTo customer, and its Address is null! I'd let the method crash and burn in these cases -- just let the exception escape (or maybe wrap it in a friendlier one).
Here is another implementation that will skip a nested property if it is an enumerator and continue deeper. Properties of type string are not affected by the Enumeration Check.
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 know if a PropertyInfo is a collection
by Berryl
I use this in a MVC project to dynamically Order my data by simply passing the Property to sort by
Example:
result = result.OrderBy((s) =>
{
return ReflectionMethods.GetPropValue("BookingItems.EventId", s);
}).ToList();
where BookingItems is a list of objects.
> Get Nest properties e.g., 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]);
}
if (info == null) { /* throw exception instead*/ }
I would actually throw an exception if they request a property that doesn't exist. The way you have it coded, if I call GetPropValue and it returns null, I don't know if that means the property didn't exist, or the property did exist but it's value was null.
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() + "'");
}
I wanted to share my solution although it may be too late. This solution is primarily to check if the nested property exists. But it can be easily tweaked to return the property value if needed.
private static PropertyInfo _GetPropertyInfo(Type type, string propertyName)
{
//***
//*** Check if the property name is a complex nested type
//***
if (propertyName.Contains("."))
{
//***
//*** Get the first property name of the complex type
//***
var tempPropertyName = propertyName.Split(".", 2);
//***
//*** Check if the property exists in the type
//***
var prop = _GetPropertyInfo(type, tempPropertyName[0]);
if (prop != null)
{
//***
//*** Drill down to check if the nested property exists in the complex type
//***
return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]);
}
else
{
return null;
}
}
else
{
return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
}
I had to refer to few posts to come up with this solution. I think this will work for multiple nested property types.
My internet connection was down when I need to solve the same problem, so I had to 're-invent 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);
}
}
Pretty sure this solves the problem for any string you use for property name, regardless of extent of nesting, as long as everything's a property.
Based on the original code from #jheddings, I have created a extension method version with generic type and verifications:
public static T GetPropertyValue<T>(this object sourceObject, string propertyName)
{
if (sourceObject == null) throw new ArgumentNullException(nameof(sourceObject));
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException(nameof(propertyName));
foreach (string currentPropertyName in propertyName.Split('.'))
{
if (string.IsNullOrWhiteSpace(currentPropertyName)) throw new InvalidOperationException($"Invalid property '{propertyName}'");
PropertyInfo propertyInfo = sourceObject.GetType().GetProperty(currentPropertyName);
if (propertyInfo == null) throw new InvalidOperationException($"Property '{currentPropertyName}' not found");
sourceObject = propertyInfo.GetValue(sourceObject);
}
return sourceObject is T result ? result : default;
}
I wrote a method that received one object type as the argument from the input and returns dictionary<string,string>
public static Dictionary<string, string> GetProperties(Type placeHolderType)
{
var result = new Dictionary<string, string>();
var properties = placeHolderType.GetProperties();
foreach (var propertyInfo in properties)
{
string name = propertyInfo.Name;
string description = GetDescriptionTitle(propertyInfo);
if (IsNonString(propertyInfo.PropertyType))
{
var list = GetProperties(propertyInfo.PropertyType);
foreach (var item in list)
{
result.Add($"{propertyInfo.PropertyType.Name}_{item.Key}", item.Value);
}
}
else
{
result.Add(name, description);
}
}
return result;
}
public static bool IsNonString(Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IPlaceHolder).IsAssignableFrom(type);
}
private static string GetDescriptionTitle(MemberInfo memberInfo)
{
if (Attribute.GetCustomAttribute(memberInfo, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
{
return descriptionAttribute.Description;
}
return memberInfo.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");
var prop = src.GetType().GetProperty(propName);
if (prop != null)
{
return prop.GetValue(src, null);
}
else
{
var props = src.GetType().GetProperties();
foreach (var property in props)
{
var propInfo = src.GetType().GetProperty(property.Name);
if (propInfo != null)
{
var propVal = propInfo.GetValue(src, null);
if (src.GetType().GetProperty(property.Name).PropertyType.IsClass)
{
return GetPropertyValue(propVal, propName);
}
return propVal;
}
}
return null;
}
usage: calling part
var emp = new Employee() { Person = new Person() { FirstName = "Ashwani" } };
var val = GetPropertyValue(emp, "FirstName");
above can search the property value at any level
Try inv.GetType().GetProperty("BillTo+Address");
Related
I need help to solve a problem, my problem is as follows, I have the following object
public class Teste
{
public string Descricao { get; set; }
public Time Time { get; set; }
}
.
public class Time
{
public string Nome { get; set; }
public Time (string nome)
{
Nome = nome;
}
}
I would like to be able to obtain the complete path of a certain property.
var teste = new Teste();
teste.Descricao = "bar";
teste.Time = new Time("foo");
var b = GetProperties(teste, "Nome");
//expected return: "Time.Nome"
I was testing something I arrived at the following method
public static IEnumerable<Tuple<string, string>> GetProperties(object obj, string propertyPath)
{
var objType = obj.GetType();
if (objType.IsValueType || objType.Equals(typeof(string)))
return Enumerable.Repeat(Tuple.Create(propertyPath, obj.ToString()), 1);
else
{
if (obj == null)
return Enumerable.Repeat(Tuple.Create(propertyPath, string.Empty), 1);
else
{
return from prop in objType.GetProperties()
where prop.CanRead && !prop.GetIndexParameters().Any()
let propValue = prop.GetValue(obj, null)
let propType = prop.PropertyType
from nameValPair in GetProperties(propValue, string.Format("{0}.{1}", propertyPath, prop.Name))
select nameValPair;
}
}
}
but it returns everything to me and I would like it to return a specific property.
I think there are some issues with searching properties that come from system modules. You have to decide which properties are worth recursively descending and which ones are not. Also, you'll have to maintain a list of objects that you have already visited to ensure that you do not follow cycles. I think a breadth-first search would be best, but for this example, I'll code a depth-first search. Also, I just return the first match, not all matches, you can adjust as needed. Furthermore, it returns a (mostly useless) string version of the path rather than a list of reflected properties that would be needed to actually access it (You'd have to do reflection again to locate the properties by name to retrieve the value from this "path" string.)
I'll start you off with a basic implementation. Likely someone else can improve upon it.
static string GetPropertyPath(object obj, string name, List<object> visited = null)
{
// does the object have the property?
Type t = obj.GetType();
var properties = t.GetProperties();
foreach (var property in properties) {
if (property.Name == name) {
// that's it!
return name;
}
}
// if we get here, it's because we didn't find the property.
if (visited == null) {
visited = new List<object>();
visited.Add(obj);
}
// Get all the properties of the first object and keep searching,
// keeping track of objects we've visited already.
foreach (var property in properties) {
// Limit which kinds of properties we search
if (object.ReferenceEquals(typeof(Program).Module, property.Module)) {
// get the value of the property
object obj2 = property.GetValue(obj);
// Do not search any previously visited objects
if (!visited.Any(o => object.ReferenceEquals(o, obj2))) {
visited.Add(obj2);
string path = GetPropertyPath(obj2, name, visited);
if (path != null) {
// found it!
return property.Name + "." + path;
}
}
}
}
return null;
}
Example
static void Main(string[] args)
{
var teste = new Teste();
teste.Descricao = "bar";
teste.Time = new Time("foo");
var b = GetPropertyPath(teste, "Nome"); // "Time.Nome"
}
I am trying implement the Data transformation using Reflection1 example in my code.
The GetSourceValue function has a switch comparing various types, but I want to remove these types and properties and have GetSourceValue get the value of the property using only a single string as the parameter. I want to pass a class and property in the string and resolve the value of the property.
Is this possible?
1 Web Archive version of original blog post
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
Of course, you will want to add validation and whatnot, but that is the gist of it.
How about something like this:
public static Object GetPropValue(this Object obj, String name) {
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;
}
public static T GetPropValue<T>(this Object obj, String name) {
Object retval = GetPropValue(obj, name);
if (retval == null) { return default(T); }
// throws InvalidCastException if types are incompatible
return (T) retval;
}
This will allow you to descend into properties using a single string, like this:
DateTime now = DateTime.Now;
int min = GetPropValue<int>(now, "TimeOfDay.Minutes");
int hrs = now.GetPropValue<int>("TimeOfDay.Hours");
You can either use these methods as static methods or extensions.
Add to any Class:
public class Foo
{
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
public string Bar { get; set; }
}
Then, you can use as:
Foo f = new Foo();
// Set
f["Bar"] = "asdf";
// Get
string s = (string)f["Bar"];
What about using the CallByName of the Microsoft.VisualBasic namespace (Microsoft.VisualBasic.dll)? It uses reflection to get properties, fields, and methods of normal objects, COM objects, and even dynamic objects.
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
and then
Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();
Great answer by jheddings. I would like to improve it by allowing referencing of aggregated arrays or collections of objects, so that propertyName could be property1.property2[X].property3:
public static object GetPropertyValue(object srcobj, string propertyName)
{
if (srcobj == null)
return null;
object obj = srcobj;
// Split property name to parts (propertyName could be hierarchical, like obj.subobj.subobj.property
string[] propertyNameParts = propertyName.Split('.');
foreach (string propertyNamePart in propertyNameParts)
{
if (obj == null) return null;
// propertyNamePart could contain reference to specific
// element (by index) inside a collection
if (!propertyNamePart.Contains("["))
{
PropertyInfo pi = obj.GetType().GetProperty(propertyNamePart);
if (pi == null) return null;
obj = pi.GetValue(obj, null);
}
else
{ // propertyNamePart is areference to specific element
// (by index) inside a collection
// like AggregatedCollection[123]
// get collection name and element index
int indexStart = propertyNamePart.IndexOf("[")+1;
string collectionPropertyName = propertyNamePart.Substring(0, indexStart-1);
int collectionElementIndex = Int32.Parse(propertyNamePart.Substring(indexStart, propertyNamePart.Length-indexStart-1));
// get collection object
PropertyInfo pi = obj.GetType().GetProperty(collectionPropertyName);
if (pi == null) return null;
object unknownCollection = pi.GetValue(obj, null);
// try to process the collection as array
if (unknownCollection.GetType().IsArray)
{
object[] collectionAsArray = unknownCollection as object[];
obj = collectionAsArray[collectionElementIndex];
}
else
{
// try to process the collection as IList
System.Collections.IList collectionAsList = unknownCollection as System.Collections.IList;
if (collectionAsList != null)
{
obj = collectionAsList[collectionElementIndex];
}
else
{
// ??? Unsupported collection type
}
}
}
}
return obj;
}
If I use the code from Ed S. I get
'ReflectionExtensions.GetProperty(Type, string)' is inaccessible due to its protection level
It seems that GetProperty() is not available in Xamarin.Forms. TargetFrameworkProfile is Profile7 in my Portable Class Library (.NET Framework 4.5, Windows 8, ASP.NET Core 1.0, Xamarin.Android, Xamarin.iOS, Xamarin.iOS Classic).
Now I found a working solution:
using System.Linq;
using System.Reflection;
public static object GetPropValue(object source, string propertyName)
{
var property = source.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase));
return property?.GetValue(source);
}
Source
About the nested properties discussion, you can avoid all the reflection stuff if you use the DataBinder.Eval Method (Object, String) as below:
var value = DataBinder.Eval(DateTime.Now, "TimeOfDay.Hours");
Of course, you'll need to add a reference to the System.Web assembly, but this probably isn't a big deal.
The method to call has changed in .NET Standard (as of 1.6). Also we can use C# 6's null conditional operator.
using System.Reflection;
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}
The below method works perfect for me:
class MyClass {
public string prop1 { set; get; }
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
}
To get the property value:
MyClass t1 = new MyClass();
...
string value = t1["prop1"].ToString();
To set the property value:
t1["prop1"] = value;
public static List<KeyValuePair<string, string>> GetProperties(object item) //where T : class
{
var result = new List<KeyValuePair<string, string>>();
if (item != null)
{
var type = item.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var pi in properties)
{
var selfValue = type.GetProperty(pi.Name).GetValue(item, null);
if (selfValue != null)
{
result.Add(new KeyValuePair<string, string>(pi.Name, selfValue.ToString()));
}
else
{
result.Add(new KeyValuePair<string, string>(pi.Name, null));
}
}
}
return result;
}
This is a way to get all properties with their values in a List.
Using PropertyInfo of the System.Reflection namespace. Reflection compiles just fine no matter what property we try to access. The error will come up during run-time.
public static object GetObjProperty(object obj, string property)
{
Type t = obj.GetType();
PropertyInfo p = t.GetProperty("Location");
Point location = (Point)p.GetValue(obj, null);
return location;
}
It works fine to get the Location property of an object
Label1.Text = GetObjProperty(button1, "Location").ToString();
We'll get the Location : {X=71,Y=27}
We can also return location.X or location.Y on the same way.
public class YourClass
{
//Add below line in your class
public object this[string propertyName] => GetType().GetProperty(propertyName)?.GetValue(this, null);
public string SampleProperty { get; set; }
}
//And you can get value of any property like this.
var value = YourClass["SampleProperty"];
The following code is a Recursive method for displaying the entire hierarchy of all of the Property Names and Values contained in an object's instance. This method uses a simplified version of AlexD's GetPropertyValue() answer above in this thread. Thanks to this discussion thread, I was able to figure out how to do this!
For example, I use this method to show an explosion or dump of all of the properties in a WebService response by calling the method as follows:
PropertyValues_byRecursion("Response", response, false);
public static object GetPropertyValue(object srcObj, string propertyName)
{
if (srcObj == null)
{
return null;
}
PropertyInfo pi = srcObj.GetType().GetProperty(propertyName.Replace("[]", ""));
if (pi == null)
{
return null;
}
return pi.GetValue(srcObj);
}
public static void PropertyValues_byRecursion(string parentPath, object parentObj, bool showNullValues)
{
/// Processes all of the objects contained in the parent object.
/// If an object has a Property Value, then the value is written to the Console
/// Else if the object is a container, then this method is called recursively
/// using the current path and current object as parameters
// Note: If you do not want to see null values, set showNullValues = false
foreach (PropertyInfo pi in parentObj.GetType().GetTypeInfo().GetProperties())
{
// Build the current object property's namespace path.
// Recursion extends this to be the property's full namespace path.
string currentPath = parentPath + "." + pi.Name;
// Get the selected property's value as an object
object myPropertyValue = GetPropertyValue(parentObj, pi.Name);
if (myPropertyValue == null)
{
// Instance of Property does not exist
if (showNullValues)
{
Console.WriteLine(currentPath + " = null");
// Note: If you are replacing these Console.Write... methods callback methods,
// consider passing DBNull.Value instead of null in any method object parameters.
}
}
else if (myPropertyValue.GetType().IsArray)
{
// myPropertyValue is an object instance of an Array of business objects.
// Initialize an array index variable so we can show NamespacePath[idx] in the results.
int idx = 0;
foreach (object business in (Array)myPropertyValue)
{
if (business == null)
{
// Instance of Property does not exist
// Not sure if this is possible in this context.
if (showNullValues)
{
Console.WriteLine(currentPath + "[" + idx.ToString() + "]" + " = null");
}
}
else if (business.GetType().IsArray)
{
// myPropertyValue[idx] is another Array!
// Let recursion process it.
PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
}
else if (business.GetType().IsSealed)
{
// Display the Full Property Path and its Value
Console.WriteLine(currentPath + "[" + idx.ToString() + "] = " + business.ToString());
}
else
{
// Unsealed Type Properties can contain child objects.
// Recurse into my property value object to process its properties and child objects.
PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
}
idx++;
}
}
else if (myPropertyValue.GetType().IsSealed)
{
// myPropertyValue is a simple value
Console.WriteLine(currentPath + " = " + myPropertyValue.ToString());
}
else
{
// Unsealed Type Properties can contain child objects.
// Recurse into my property value object to process its properties and child objects.
PropertyValues_byRecursion(currentPath, myPropertyValue, showNullValues);
}
}
}
public static TValue GetFieldValue<TValue>(this object instance, string name)
{
var type = instance.GetType();
var field = type.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.FieldType) && e.Name == name);
return (TValue)field?.GetValue(instance);
}
public static TValue GetPropertyValue<TValue>(this object instance, string name)
{
var type = instance.GetType();
var field = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.PropertyType) && e.Name == name);
return (TValue)field?.GetValue(instance);
}
Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)
Here is another way to find a nested property that doesn't require the string to tell you the nesting path. Credit to Ed S. for the single property method.
public static T FindNestedPropertyValue<T, N>(N model, string propName) {
T retVal = default(T);
bool found = false;
PropertyInfo[] properties = typeof(N).GetProperties();
foreach (PropertyInfo property in properties) {
var currentProperty = property.GetValue(model, null);
if (!found) {
try {
retVal = GetPropValue<T>(currentProperty, propName);
found = true;
} catch { }
}
}
if (!found) {
throw new Exception("Unable to find property: " + propName);
}
return retVal;
}
public static T GetPropValue<T>(object srcObject, string propName) {
return (T)srcObject.GetType().GetProperty(propName).GetValue(srcObject, null);
}
You never mention what object you are inspecting, and since you are rejecting ones that reference a given object, I will assume you mean a static one.
using System.Reflection;
public object GetPropValue(string prop)
{
int splitPoint = prop.LastIndexOf('.');
Type type = Assembly.GetEntryAssembly().GetType(prop.Substring(0, splitPoint));
object obj = null;
return type.GetProperty(prop.Substring(splitPoint + 1)).GetValue(obj, null);
}
Note that I marked the object that is being inspected with the local variable obj. null means static, otherwise set it to what you want. Also note that the GetEntryAssembly() is one of a few available methods to get the "running" assembly, you may want to play around with it if you are having a hard time loading the type.
Have a look at the Heleonix.Reflection library. You can get/set/invoke members by paths, or create a getter/setter (lambda compiled into a delegate) which is faster than reflection. For example:
var success = Reflector.Get(DateTime.Now, null, "Date.Year", out int value);
Or create a getter once and cache for reuse (this is more performant but might throw NullReferenceException if an intermediate member is null):
var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
Or if you want to create a List<Action<object, object>> of different getters, just specify base types for compiled delegates (type conversions will be added into compiled lambdas):
var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
Although the original question was about how to get the value of the property using only a single string as the parameter, it makes a lot of sense here to use an Expression rather than simply a string to ensure that the caller never uses a hard coded property name. Here is a one line version with usage:
public static class Utils
...
public static TVal GetPropertyValue<T, TVal>(T t, Expression<Func<T, TVal>> x)
=> (TVal)((x.Body as MemberExpression)?.Member as PropertyInfo)!.GetValue(t);
...
var val = Utils.GetPropertyValue(foo, p => p.Bar);
Here is a slightly better version in terms of readability a error handling:
public static TVal GetPropertyValue<T, TVal>(T t, Expression<Func<T, TVal>> x)
{
var m = (x.Body as MemberExpression)?.Member;
var p = m as PropertyInfo;
if (null == p)
throw new ArgumentException($"Unknown property: {typeof(T).Name}.{(m?.Name??"???")}");
return (TVal)p.GetValue(t);
}
In short you pass in a lambda expression reading a property. The body of the lambda - the part on the right of the fat arrow - is a member expression from which you can get the member name and which you can cast to a PropertyInfo, provided the member is actually a Property and not, for instance, a method.
In the short version, the null forgiving operator - the ! in the expression - tells the compiler that the PropertyInfo will not be null. This is a big lie and you will get a NullReferenceException at runtime. The longer version gives you the name of the property if it manages to get it.
PS: Thanks to Oleg G. for the initial version of this code :)
shorter way ....
var a = new Test { Id = 1 , Name = "A" , date = DateTime.Now};
var b = new Test { Id = 1 , Name = "AXXX", date = DateTime.Now };
var compare = string.Join("",a.GetType().GetProperties().Select(x => x.GetValue(a)).ToArray())==
string.Join("",b.GetType().GetProperties().Select(x => x.GetValue(b)).ToArray());
jheddings and AlexD both wrote excellent answers on how to resolve property strings. I'd like to throw mine in the mix, since I wrote a dedicated library exactly for that purpose.
Pather.CSharp's main class is Resolver. Per default it can resolve properties, array and dictionary entries.
So, for example, if you have an object like this
var o = new { Property1 = new { Property2 = "value" } };
and want to get Property2, you can do it like this:
IResolver resolver = new Resolver();
var path = "Property1.Property2";
object result = r.Resolve(o, path);
//=> "value"
This is the most basic example of the paths it can resolve. If you want to see what else it can, or how you can extend it, just head to its Github page.
Here's what I got based on other answers. A little overkill on getting so specific with the error handling.
public static T GetPropertyValue<T>(object sourceInstance, string targetPropertyName, bool throwExceptionIfNotExists = false)
{
string errorMsg = null;
try
{
if (sourceInstance == null || string.IsNullOrWhiteSpace(targetPropertyName))
{
errorMsg = $"Source object is null or property name is null or whitespace. '{targetPropertyName}'";
Log.Warn(errorMsg);
if (throwExceptionIfNotExists)
throw new ArgumentException(errorMsg);
else
return default(T);
}
Type returnType = typeof(T);
Type sourceType = sourceInstance.GetType();
PropertyInfo propertyInfo = sourceType.GetProperty(targetPropertyName, returnType);
if (propertyInfo == null)
{
errorMsg = $"Property name '{targetPropertyName}' of type '{returnType}' not found for source object of type '{sourceType}'";
Log.Warn(errorMsg);
if (throwExceptionIfNotExists)
throw new ArgumentException(errorMsg);
else
return default(T);
}
return (T)propertyInfo.GetValue(sourceInstance, null);
}
catch(Exception ex)
{
errorMsg = $"Problem getting property name '{targetPropertyName}' from source instance.";
Log.Error(errorMsg, ex);
if (throwExceptionIfNotExists)
throw;
}
return default(T);
}
Here is my solution. It works also with COM objects and allows to access collection/array items from COM objects.
public static object GetPropValue(this object obj, string name)
{
foreach (string part in name.Split('.'))
{
if (obj == null) { return null; }
Type type = obj.GetType();
if (type.Name == "__ComObject")
{
if (part.Contains('['))
{
string partWithoundIndex = part;
int index = ParseIndexFromPropertyName(ref partWithoundIndex);
obj = Versioned.CallByName(obj, partWithoundIndex, CallType.Get, index);
}
else
{
obj = Versioned.CallByName(obj, part, CallType.Get);
}
}
else
{
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
}
return obj;
}
private static int ParseIndexFromPropertyName(ref string name)
{
int index = -1;
int s = name.IndexOf('[') + 1;
int e = name.IndexOf(']');
if (e < s)
{
throw new ArgumentException();
}
string tmp = name.Substring(s, e - s);
index = Convert.ToInt32(tmp);
name = name.Substring(0, s - 1);
return index;
}
Whenever you want to loop over all properties in on an object and then use each value of the property must use this piece of code:
foreach (var property in request.GetType().GetProperties())
{
var valueOfProperty = property.GetValue(properties, null);
}
OK So I'm using the code below which I found in stack overflow.
// so we can use reflection to access the object properties
public static Object GetPropValue(this Object obj, String name)
{
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;
}
// so we can use reflection to access the object properties
public static T GetPropValue<T>(this Object obj, String name)
{
Object retval = GetPropValue(obj, name);
if (retval == null) { return default(T); }
// throws InvalidCastException if types are incompatible
return (T)retval;
}
To get access to a object property using a string. So I can do stuff like this:
DateTime.Now.GetPropValue<int>("TimeOfDay.Hours")
which works well.
But my class doesn't use properties and I cannot change it as it is part of a library. I believe the it is using fields instead of properties (I didn't know they were treated differently in C# till now!). How do I access these field names and properties together? i.e for a class like this
class Student
{
public string name {get; set;} // property
public int age; // field
public Info info; // field
}
class Info
{
string a {get; set;} // property
string b; // fields
}
So I can access using this:
Student a;
a.GetPropValue<string>("info.a"); // getting a property which works!
a.GetPropValue<string>("info.b"); // getting a field value which doesn't!
As soon as I posted this I figured it out and here is my modified function
// so we can use reflection to access the object properties
public static Object GetPropertyOrFieldValue(this Object obj, String name)
{
foreach (String part in name.Split('.'))
{
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo propertyInfo = type.GetProperty(part);
FieldInfo fieldInfo = type.GetField(part);
if (propertyInfo == null && fieldInfo == null)
{
return null;
}
obj = (propertyInfo != null) ? propertyInfo.GetValue(obj, null) : (fieldInfo != null) ? fieldInfo.GetValue(obj) : null;
}
return obj;
}
// so we can use reflection to access the object properties
public static T GetPropertyOrFieldValue<T>(this Object obj, String name)
{
Object retval = GetPropertyOrFieldValue(obj, name);
if (retval == null) { return default(T); }
// throws InvalidCastException if types are incompatible
return (T)retval;
}
Here is the equivalent code to get a field instead of a property:
public Object GetFieldValue(this Object obj, String name)
{
foreach (String part in name.Split('.'))
{
if (obj == null) { return null; }
Type type = obj.GetType();
FieldInfo info = type.GetField(part);
if (info == null) { return null; }
obj = info.GetValue(obj);
}
return obj;
}
// so we can use reflection to access the object properties
public T GetFieldValue<T>(this Object obj, String name)
{
Object retval = GetFieldValue(obj, name);
if (retval == null) { return default(T); }
// throws InvalidCastException if types are incompatible
return (T)retval;
}
I am trying to sort a list of objects, the code in my else statements works like charm but there is a column that has nested property and that's where the problem occurs. when I try to get the value of the nested property it gives me null exception. Can you help me globalize the code so I don't have this extra if in my code?
if (e.SortExpression == "Distribution.Coder.Name")
lstActivities = lstActivities.OrderByDescending(o => (string)o.Distribution.Coder.Name).ToList();
else
lstActivities = lstActivities.OrderByDescending(o => typeof(Activity).GetProperty(e.SortExpression).GetValue(o, null)).ToList();
Just want to shared with you this code what I found. I do not remember it reference.
public static object GetPropertyValue(object obj, string name)
{
if (string.IsNullOrEmpty(name))
{
return 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;
}
And using:
GetPropertyValue(Distribution, "Coder.Name")
Closed. This question is opinion-based. It is not currently accepting answers.
Closed 4 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
This is what I've come up with as a method on a class inherited by many of my other classes. The idea is that it allows the simple comparison between properties of Objects of the same Type.
Now, this does work - but in the interest of improving the quality of my code I thought I'd throw it out for scrutiny. How can it be better/more efficient/etc.?
/// <summary>
/// Compare property values (as strings)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public bool PropertiesEqual(object comparisonObject)
{
Type sourceType = this.GetType();
Type destinationType = comparisonObject.GetType();
if (sourceType == destinationType)
{
PropertyInfo[] sourceProperties = sourceType.GetProperties();
foreach (PropertyInfo pi in sourceProperties)
{
if ((sourceType.GetProperty(pi.Name).GetValue(this, null) == null && destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null) == null))
{
// if both are null, don't try to compare (throws exception)
}
else if (!(sourceType.GetProperty(pi.Name).GetValue(this, null).ToString() == destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null).ToString()))
{
// only need one property to be different to fail Equals.
return false;
}
}
}
else
{
throw new ArgumentException("Comparison object must be of the same type.","comparisonObject");
}
return true;
}
I was looking for a snippet of code that would do something similar to help with writing unit test. Here is what I ended up using.
public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(ignore);
foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
{
if (!ignoreList.Contains(pi.Name))
{
object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
object toValue = type.GetProperty(pi.Name).GetValue(to, null);
if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
{
return false;
}
}
}
return true;
}
return self == to;
}
EDIT:
Same code as above but uses LINQ and Extension methods :
public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = typeof(T);
var ignoreList = new List<string>(ignore);
var unequalProperties =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name) && pi.GetUnderlyingType().IsSimpleType() && pi.GetIndexParameters().Length == 0
let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
let toValue = type.GetProperty(pi.Name).GetValue(to, null)
where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
select selfValue;
return !unequalProperties.Any();
}
return self == to;
}
public static class TypeExtensions
{
/// <summary>
/// Determine whether a type is simple (String, Decimal, DateTime, etc)
/// or complex (i.e. custom class with public properties and methods).
/// </summary>
/// <see cref="http://stackoverflow.com/questions/2442534/how-to-test-if-type-is-primitive"/>
public static bool IsSimpleType(
this Type type)
{
return
type.IsValueType ||
type.IsPrimitive ||
new[]
{
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
(Convert.GetTypeCode(type) != TypeCode.Object);
}
public static Type GetUnderlyingType(this MemberInfo member)
{
switch (member.MemberType)
{
case MemberTypes.Event:
return ((EventInfo)member).EventHandlerType;
case MemberTypes.Field:
return ((FieldInfo)member).FieldType;
case MemberTypes.Method:
return ((MethodInfo)member).ReturnType;
case MemberTypes.Property:
return ((PropertyInfo)member).PropertyType;
default:
throw new ArgumentException
(
"Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
);
}
}
}
UPDATE: The latest version of Compare-Net-Objects is located on GitHub , has NuGet package and Tutorial. It can be called like
//This is the comparison class
CompareLogic compareLogic = new CompareLogic();
ComparisonResult result = compareLogic.Compare(person1, person2);
//These will be different, write out the differences
if (!result.AreEqual)
Console.WriteLine(result.DifferencesString);
Or if you need to change some configuration, use
CompareLogic basicComparison = new CompareLogic()
{ Config = new ComparisonConfig()
{ MaxDifferences = propertyCount
//add other configurations
}
};
Full list of configurable parameters is in ComparisonConfig.cs
Original answer:
The limitations I see in your code:
The biggest one is that it doesn't do a deep object comparison.
It doesn't do an element by element comparison in case properties are lists or contain lists as elements (this can go n-levels).
It doesn't take into account that some type of properties should not be compared (e.g. a Func property used for filtering purposes, like the one in the PagedCollectionView class).
It doesn't keep track of what properties actually were different (so you can show in your assertions).
I was looking today for some solution for unit-testing purposes to do property by property deep comparison and I ended up using: http://comparenetobjects.codeplex.com.
It is a free library with just one class which you can simply use like this:
var compareObjects = new CompareObjects()
{
CompareChildren = true, //this turns deep compare one, otherwise it's shallow
CompareFields = false,
CompareReadOnly = true,
ComparePrivateFields = false,
ComparePrivateProperties = false,
CompareProperties = true,
MaxDifferences = 1,
ElementsToIgnore = new List<string>() { "Filter" }
};
Assert.IsTrue(
compareObjects.Compare(objectA, objectB),
compareObjects.DifferencesString
);
Also, it can be easily re-compiled for Silverlight. Just copy the one class into a Silverlight project and remove one or two lines of code for comparisons that are not available in Silverlight, like private members comparison.
I think it would be best to follow the pattern for Override Object#Equals()
For a better description: Read Bill Wagner's Effective C# - Item 9 I think
public override Equals(object obOther)
{
if (null == obOther)
return false;
if (object.ReferenceEquals(this, obOther)
return true;
if (this.GetType() != obOther.GetType())
return false;
# private method to compare members.
return CompareMembers(this, obOther as ThisClass);
}
Also in methods that check for equality, you should return either true or false. either they are equal or they are not.. instead of throwing an exception, return false.
I'd consider overriding Object#Equals.
Even though you must have considered this, using Reflection to compare properties is supposedly slow (I dont have numbers to back this up). This is the default behavior for valueType#Equals in C# and it is recommended that you override Equals for value types and do a member wise compare for performance. (Earlier I speed-read this as you have a collection of custom Property objects... my bad.)
Update-Dec 2011:
Of course, if the type already has a production Equals() then you need another approach.
If you're using this to compare immutable data structures exclusively for test purposes, you shouldn't add an Equals to production classes (Someone might hose the tests by chainging the Equals implementation or you may prevent creation of a production-required Equals implementation).
If performance doesn't matter, you could serialize them and compare the results:
var serializer = new XmlSerializer(typeof(TheObjectType));
StringWriter serialized1 = new StringWriter(), serialized2 = new StringWriter();
serializer.Serialize(serialized1, obj1);
serializer.Serialize(serialized2, obj2);
bool areEqual = serialized1.ToString() == serialized2.ToString();
I think the answer of Big T was quite good but the deep comparison was missing, so I tweaked it a little bit:
using System.Collections.Generic;
using System.Reflection;
/// <summary>Comparison class.</summary>
public static class Compare
{
/// <summary>Compare the public instance properties. Uses deep comparison.</summary>
/// <param name="self">The reference object.</param>
/// <param name="to">The object to compare.</param>
/// <param name="ignore">Ignore property with name.</param>
/// <typeparam name="T">Type of objects.</typeparam>
/// <returns><see cref="bool">True</see> if both objects are equal, else <see cref="bool">false</see>.</returns>
public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = self.GetType();
var ignoreList = new List<string>(ignore);
foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (ignoreList.Contains(pi.Name))
{
continue;
}
var selfValue = type.GetProperty(pi.Name).GetValue(self, null);
var toValue = type.GetProperty(pi.Name).GetValue(to, null);
if (pi.PropertyType.IsClass && !pi.PropertyType.Module.ScopeName.Equals("CommonLanguageRuntimeLibrary"))
{
// Check of "CommonLanguageRuntimeLibrary" is needed because string is also a class
if (PublicInstancePropertiesEqual(selfValue, toValue, ignore))
{
continue;
}
return false;
}
if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
{
return false;
}
}
return true;
}
return self == to;
}
}
I would add the following line to the PublicInstancePropertiesEqual method to avoid copy & paste errors:
Assert.AreNotSame(self, to);
Do you override .ToString() on all of your objects that are in the properties? Otherwise, that second comparison could come back with null.
Also, in that second comparison, I'm on the fence about the construct of !( A == B) compared to (A != B), in terms of readability six months/two years from now. The line itself is pretty wide, which is ok if you've got a wide monitor, but might not print out very well. (nitpick)
Are all of your objects always using properties such that this code will work? Could there be some internal, non-propertied data that could be different from one object to another, but all exposed data is the same? I'm thinking of some data which could change over time, like two random number generators that happen to hit the same number at one point, but are going to produce two different sequences of information, or just any data that doesn't get exposed through the property interface.
If you are only comparing objects of the same type or further down the inheritance chain, why not specify the parameter as your base type, rather than object ?
Also do null checks on the parameter as well.
Furthermore I'd make use of 'var' just to make the code more readable (if its c#3 code)
Also, if the object has reference types as properties then you are just calling ToString() on them which doesn't really compare values. If ToString isn't overwridden then its just going to return the type name as a string which could return false-positives.
The first thing I would suggest would be to split up the actual comparison so that it's a bit more readable (I've also taken out the ToString() - is that needed?):
else {
object originalProperty = sourceType.GetProperty(pi.Name).GetValue(this, null);
object comparisonProperty = destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null);
if (originalProperty != comparisonProperty)
return false;
The next suggestion would be to minimise the use of reflection as much as possible - it's really slow. I mean, really slow. If you are going to do this, I would suggest caching the property references. I'm not intimately familiar with the Reflection API, so if this is a bit off, just adjust to make it compile:
// elsewhere
Dictionary<object, Property[]> lookupDictionary = new Dictionary<object, Property[]>;
Property[] objectProperties = null;
if (lookupDictionary.ContainsKey(sourceType)) {
objectProperties = lookupProperties[sourceType];
} else {
// build array of Property references
PropertyInfo[] sourcePropertyInfos = sourceType.GetProperties();
Property[] sourceProperties = new Property[sourcePropertyInfos.length];
for (int i=0; i < sourcePropertyInfos.length; i++) {
sourceProperties[i] = sourceType.GetProperty(pi.Name);
}
// add to cache
objectProperties = sourceProperties;
lookupDictionary[object] = sourceProperties;
}
// loop through and compare against the instances
However, I have to say that I agree with the other posters. This smells lazy and inefficient. You should be implementing IComparable instead :-).
here is revised one to treat null = null as equal
private bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(ignore);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!ignoreList.Contains(pi.Name))
{
object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
object toValue = type.GetProperty(pi.Name).GetValue(to, null);
if (selfValue != null)
{
if (!selfValue.Equals(toValue))
return false;
}
else if (toValue != null)
return false;
}
}
return true;
}
return self == to;
}
I ended up doing this:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b)
{
int count = a.GetType().GetProperties().Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe();
bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb)
{
return false;
}
}
return true;
}
Usage:
if (Compare<ObjectType>(a, b))
Update
If you want to ignore some properties by name:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b, params string[] ignore)
{
int count = a.GetType().GetProperties().Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe();
bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb && ignore.Where(x => x == a.GetType().GetProperties()[i].Name).Count() == 0)
{
return false;
}
}
return true;
}
Usage:
if (MyFunction.Compare<ObjType>(a, b, "Id","AnotherProp"))
You can optimize your code by calling GetProperties only once per type:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b, params string[] ignore)
{
var aProps = a.GetType().GetProperties();
var bProps = b.GetType().GetProperties();
int count = aProps.Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = aProps[i].GetValue(a, null).ToStringNullSafe();
bb = bProps[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb && ignore.Where(x => x == aProps[i].Name).Count() == 0)
{
return false;
}
}
return true;
}
For completeness I want to add reference to
http://www.cyotek.com/blog/comparing-the-properties-of-two-objects-via-reflection
It has more complete logic than most of others answers on this page.
However I prefer Compare-Net-Objects library
https://github.com/GregFinzer/Compare-Net-Objects (referred by Liviu Trifoi's answer)
The library has NuGet package http://www.nuget.org/packages/CompareNETObjects and multiple options to configure.
Make sure objects aren't null.
Having obj1 and obj2:
if(obj1 == null )
{
return false;
}
return obj1.Equals( obj2 );
This works even if the objects are different. you could customize the methods in the utilities class maybe you want to compare private properties as well...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
class ObjectA
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public DateTime PropertyD { get; set; }
public string FieldA;
public DateTime FieldB;
}
class ObjectB
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public DateTime PropertyD { get; set; }
public string FieldA;
public DateTime FieldB;
}
class Program
{
static void Main(string[] args)
{
// create two objects with same properties
ObjectA a = new ObjectA() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
ObjectB b = new ObjectB() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
// add fields to those objects
a.FieldA = "hello";
b.FieldA = "Something differnt";
if (a.ComparePropertiesTo(b))
{
Console.WriteLine("objects have the same properties");
}
else
{
Console.WriteLine("objects have diferent properties!");
}
if (a.CompareFieldsTo(b))
{
Console.WriteLine("objects have the same Fields");
}
else
{
Console.WriteLine("objects have diferent Fields!");
}
Console.Read();
}
}
public static class Utilities
{
public static bool ComparePropertiesTo(this Object a, Object b)
{
System.Reflection.PropertyInfo[] properties = a.GetType().GetProperties(); // get all the properties of object a
foreach (var property in properties)
{
var propertyName = property.Name;
var aValue = a.GetType().GetProperty(propertyName).GetValue(a, null);
object bValue;
try // try to get the same property from object b. maybe that property does
// not exist!
{
bValue = b.GetType().GetProperty(propertyName).GetValue(b, null);
}
catch
{
return false;
}
if (aValue == null && bValue == null)
continue;
if (aValue == null && bValue != null)
return false;
if (aValue != null && bValue == null)
return false;
// if properties do not match return false
if (aValue.GetHashCode() != bValue.GetHashCode())
{
return false;
}
}
return true;
}
public static bool CompareFieldsTo(this Object a, Object b)
{
System.Reflection.FieldInfo[] fields = a.GetType().GetFields(); // get all the properties of object a
foreach (var field in fields)
{
var fieldName = field.Name;
var aValue = a.GetType().GetField(fieldName).GetValue(a);
object bValue;
try // try to get the same property from object b. maybe that property does
// not exist!
{
bValue = b.GetType().GetField(fieldName).GetValue(b);
}
catch
{
return false;
}
if (aValue == null && bValue == null)
continue;
if (aValue == null && bValue != null)
return false;
if (aValue != null && bValue == null)
return false;
// if properties do not match return false
if (aValue.GetHashCode() != bValue.GetHashCode())
{
return false;
}
}
return true;
}
}
Update on Liviu's answer above - CompareObjects.DifferencesString has been deprecated.
This works well in a unit test:
CompareLogic compareLogic = new CompareLogic();
ComparisonResult result = compareLogic.Compare(object1, object2);
Assert.IsTrue(result.AreEqual);
This method will get properties of the class and compare the values for each property. If any of the values are different, it will return false, else it will return true.
public static bool Compare<T>(T Object1, T object2)
{
//Get the type of the object
Type type = typeof(T);
//return false if any of the object is false
if (Object1 == null || object2 == null)
return false;
//Loop through each properties inside class and get values for the property from both the objects and compare
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
if (property.Name != "ExtensionData")
{
string Object1Value = string.Empty;
string Object2Value = string.Empty;
if (type.GetProperty(property.Name).GetValue(Object1, null) != null)
Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString();
if (type.GetProperty(property.Name).GetValue(object2, null) != null)
Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString();
if (Object1Value.Trim() != Object2Value.Trim())
{
return false;
}
}
}
return true;
}
Usage:
bool isEqual = Compare<Employee>(Object1, Object2)
To expand on #nawfal:s answer, I use this to test objects of different types in my unit tests to compare equal property names. In my case database entity and DTO.
Used like this in my test;
Assert.IsTrue(resultDto.PublicInstancePropertiesEqual(expectedEntity));
public static bool PublicInstancePropertiesEqual<T, Z>(this T self, Z to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = typeof(T);
var type2 = typeof(Z);
var ignoreList = new List<string>(ignore);
var unequalProperties =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name)
let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
let toValue = type2.GetProperty(pi.Name).GetValue(to, null)
where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
select selfValue;
return !unequalProperties.Any();
}
return self == null && to == null;
}
sometimes you don't want to compare all public properties and want to compare only the subset of them, so in this case you can just move logic to compare the desired list of properties to abstract class
public abstract class ValueObject<T> where T : ValueObject<T>
{
protected abstract IEnumerable<object> GetAttributesToIncludeInEqualityCheck();
public override bool Equals(object other)
{
return Equals(other as T);
}
public bool Equals(T other)
{
if (other == null)
{
return false;
}
return GetAttributesToIncludeInEqualityCheck()
.SequenceEqual(other.GetAttributesToIncludeInEqualityCheck());
}
public static bool operator ==(ValueObject<T> left, ValueObject<T> right)
{
return Equals(left, right);
}
public static bool operator !=(ValueObject<T> left, ValueObject<T> right)
{
return !(left == right);
}
public override int GetHashCode()
{
int hash = 17;
foreach (var obj in this.GetAttributesToIncludeInEqualityCheck())
hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode());
return hash;
}
}
and use this abstract class later to compare the objects
public class Meters : ValueObject<Meters>
{
...
protected decimal DistanceInMeters { get; private set; }
...
protected override IEnumerable<object> GetAttributesToIncludeInEqualityCheck()
{
return new List<Object> { DistanceInMeters };
}
}
my solution inspired from Aras Alenin answer above where I added one level of object comparison and a custom object for comparison results. I am also interested to get property name with object name:
public static IEnumerable<ObjectPropertyChanged> GetPublicSimplePropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored) where T : class
{
return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, true, null, null);
}
public static IReadOnlyList<ObjectPropertyChanged> GetPublicGenericPropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored) where T : class
{
return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, false, null, null);
}
/// <summary>
/// Gets the names of the public properties which values differs between first and second objects.
/// Considers 'simple' properties AND for complex properties without index, get the simple properties of the children objects.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="previous">The previous object.</param>
/// <param name="proposedChange">The second object which should be the new one.</param>
/// <param name="namesOfPropertiesToBeIgnored">The names of the properties to be ignored.</param>
/// <param name="simpleTypeOnly">if set to <c>true</c> consider simple types only.</param>
/// <param name="parentTypeString">The parent type string. Meant only for recursive call with simpleTypeOnly set to <c>true</c>.</param>
/// <param name="secondType">when calling recursively, the current type of T must be clearly defined here, as T will be more generic (using base class).</param>
/// <returns>
/// the names of the properties
/// </returns>
private static IReadOnlyList<ObjectPropertyChanged> GetPublicGenericPropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored, bool simpleTypeOnly, string parentTypeString, Type secondType) where T : class
{
List<ObjectPropertyChanged> propertiesChanged = new List<ObjectPropertyChanged>();
if (previous != null && proposedChange != null)
{
var type = secondType == null ? typeof(T) : secondType;
string typeStr = parentTypeString + type.Name + ".";
var ignoreList = namesOfPropertiesToBeIgnored.CreateList();
IEnumerable<IEnumerable<ObjectPropertyChanged>> genericPropertiesChanged =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name) && pi.GetIndexParameters().Length == 0
&& (!simpleTypeOnly || simpleTypeOnly && pi.PropertyType.IsSimpleType())
let firstValue = type.GetProperty(pi.Name).GetValue(previous, null)
let secondValue = type.GetProperty(pi.Name).GetValue(proposedChange, null)
where firstValue != secondValue && (firstValue == null || !firstValue.Equals(secondValue))
let subPropertiesChanged = simpleTypeOnly || pi.PropertyType.IsSimpleType()
? null
: GetPublicGenericPropertiesChanged(firstValue, secondValue, namesOfPropertiesToBeIgnored, true, typeStr, pi.PropertyType)
let objectPropertiesChanged = subPropertiesChanged != null && subPropertiesChanged.Count() > 0
? subPropertiesChanged
: (new ObjectPropertyChanged(proposedChange.ToString(), typeStr + pi.Name, firstValue.ToStringOrNull(), secondValue.ToStringOrNull())).CreateList()
select objectPropertiesChanged;
if (genericPropertiesChanged != null)
{ // get items from sub lists
genericPropertiesChanged.ForEach(a => propertiesChanged.AddRange(a));
}
}
return propertiesChanged;
}
Using the following class to store comparison results
[System.Serializable]
public class ObjectPropertyChanged
{
public ObjectPropertyChanged(string objectId, string propertyName, string previousValue, string changedValue)
{
ObjectId = objectId;
PropertyName = propertyName;
PreviousValue = previousValue;
ProposedChangedValue = changedValue;
}
public string ObjectId { get; set; }
public string PropertyName { get; set; }
public string PreviousValue { get; set; }
public string ProposedChangedValue { get; set; }
}
And a sample unit test:
[TestMethod()]
public void GetPublicGenericPropertiesChangedTest1()
{
// Define objects to test
Function func1 = new Function { Id = 1, Description = "func1" };
Function func2 = new Function { Id = 2, Description = "func2" };
FunctionAssignment funcAss1 = new FunctionAssignment
{
Function = func1,
Level = 1
};
FunctionAssignment funcAss2 = new FunctionAssignment
{
Function = func2,
Level = 2
};
// Main test: read properties changed
var propertiesChanged = Utils.GetPublicGenericPropertiesChanged(funcAss1, funcAss2, null);
Assert.IsNotNull(propertiesChanged);
Assert.IsTrue(propertiesChanged.Count == 3);
Assert.IsTrue(propertiesChanged[0].PropertyName == "FunctionAssignment.Function.Description");
Assert.IsTrue(propertiesChanged[1].PropertyName == "FunctionAssignment.Function.Id");
Assert.IsTrue(propertiesChanged[2].PropertyName == "FunctionAssignment.Level");
}