Related
Update: We are familiar with Automapper but we have code that maps our data tables to models and will be easy to add the SqlTable attribute. And thus we want to be able to do this with simple extensions versus having to do it through Automapper.
Our legacy database has some bad naming practices. In creating our DateModels, we’ve got rid of a lot of the prefixes and extensions.
What we’re working on is a way to take the EF entities we get back from the database and using reflection copy the property values over into our DataModels.
For a simple class, we have everything work. The issue we haven’t figured out just yet is how to handle collections.
A sample of our sql tables would be.
Customer Table
Cust_Id
Cust_Name
Cust_ProductId - FK to Product.Id
Product Table
Product_Id
Product_Name
Then our data models would be
public class CustomerModel : BaseCustomerModel
{
[SLSqlTable("Cust_Id")]
public int Id { get; set; }
[SLSqlTable("Cust_Name")]
public string Name { get; set; }
[SLSqlTable("Cust_ProductId")]
public string ProductId { get; set; }
[SLSqlTable("Products")]
public IList<BaseProduct> Products { get; set; }
}
public class BaseProductModel
{
[SLSqlTable("Product_Id")]
public int? Id { get; set; }
[SLSqlTable("Product_Name")]
public string Name { get; set; }
}
We’re using the SLSqlTableAttribute we created to map the names.
Then from the internet, we’re using the following code to copy the data between properties. It works fine for everything except our collections right now and that’s what we’re trying to figure out. We were think, we detect a collection and then some how just recursively call back into CopyToModelProperties.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static T CopyToModelProperties<T>(this object source) where T : class, new()
{
T destination = new T();
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
// Collect all the valid properties to map
var results = from srcProp in typeSrc.GetProperties()
let targetProperty = typeDest.GetProperty(GetModelPropertyNameFromSqlAttribute(typeDest, srcProp.Name))
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
select new { sourceProperty = srcProp, targetProperty = targetProperty };
//map the properties
foreach (var props in results)
{
if (props.targetProperty.PropertyType.IsGenericType)
{
var _targetType = props.targetProperty.PropertyType.GetGenericArguments()[0];
// props.sourceProperty.PropertyType.MakeGenericType(_targetType).CopyToModelProperties((dynamic)List<>);
}
else
{
props.targetProperty.SetValue(destination, props.sourceProperty.GetValue(source, null), null);
}
}
return destination;
}
public static string GetModelPropertyNameFromSqlAttribute(Type model, string sqlName)
{
string _ret = "";
var _property = model.GetProperties().Where(w => w.GetCustomAttributes<SLSqlTableAttribute>().Where(w1 => w1.FieldName == sqlName).Any()).FirstOrDefault();
if(_property != null)
{
_ret = _property.Name;
}
return _ret;
}
Here is a sample of code getting customers with all their products.
using (var _dc = new BulkTestEntities())
{
var _custs = _dc.Customers.Include("Products").ToList();
foreach(var cust in _custs)
{
_resp.Add(cust.CopyToModelProperties<CustomerModel>());
}
}
This works fine and the if condition checking IsGenericType works, we just need to figure out what code goes here that handle the collection of products when getting customer back.
We thought it would be a recursive call back to CopyToModelProperties because Products could have a collection inside it as well and that could have a collection so we don’t want to hard code levels.
So the question is how to take props.sourceProperty from the if condition above and copy a collection of SQL Entities over to a collection of DataModels?
I haven't used reflection in a long time but kind of shock something like this wasn't out there or no response.
Digging on the internet, found some code snippets that I pieced together to come up with this. It needs some tweaking but it handles the task we set out to do. Also, not going to handle all scenarios most likely but good enough for us to start and build on. Also not all the PITA configuration with something like AutoMapper. We wrote a program that goes out and gets our sql tables, generates classes and properties and we can easily add our attribute to each property and now all we do is call the functions below to map data.
Code we have to that would use what we came up with.
using (var _dc = new BulkTestEntities())
{
var _custs = _dc.Customers.Include("Products").Include("CustomerType").ToList();
// CopyPropertyData.CopyObjectPropertyData(_custs, _resp);
//foreach(var cust in _custs)
//{
// _resp.Add(cust.CopyToModelProperties<CustomerModel>());
//}
foreach (var cust in _custs)
{
CustomerModel _newCust = new CustomerModel();
SQLExtensions.CopyToModelProperties(cust, _newCust);
// CopyData.CopyObjectData(cust, _newCust);
_resp.Add(_newCust);
}
}
Here are the static classes that will handle an IEnumerable and class.
public static void CopyToModelProperties(object source, object target)
{
Type _typeTarget = target.GetType();
Type _typeSrc = source.GetType();
if (_typeSrc.GetInterfaces().Any(a => a.Name == "IEnumerable") && _typeSrc.IsGenericType && _typeTarget.IsGenericType)
{
// Dynamic allows us to loop through a collection if it's type IEnumerable
dynamic _sourceList = source;
foreach (var _source in _sourceList)
{
// Create a temp class of the generic type of the target list
object _tempTarget = Activator.CreateInstance(_typeTarget.GetGenericArguments()[0]);
//Recursively call to map all child properties
CopyToModelProperties(_source, _tempTarget);
// Add to target collection passed in
target.GetType().GetMethod("Add").Invoke(target, new[] { _tempTarget });
}
}
else
{
var results = from srcProp in _typeSrc.GetProperties()
let targetProperty = _typeTarget.GetProperty(GetModelPropertyNameFromSqlAttribute(_typeTarget, srcProp.Name))
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
// && targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new { sourceProperty = srcProp, targetProperty = targetProperty };
foreach (var prop in results)
{
if (prop.targetProperty.CanWrite && prop.sourceProperty.CanRead)
{
object targetValue = prop.targetProperty.GetValue(target, null);
object sourceValue = prop.sourceProperty.GetValue(source, null);
if (sourceValue == null) { continue; }
//if (prop.sourceProperty.PropertyType.IsArray
// && prop.targetProperty.PropertyType.IsArray
// && sourceValue != null)
if(prop.sourceProperty.PropertyType.IsClass && prop.targetProperty.PropertyType != typeof(string))
{
var destinationClass = Activator.CreateInstance(prop.targetProperty.PropertyType);
object copyValue = prop.sourceProperty.GetValue(source);
CopyToModelProperties(copyValue, destinationClass);
prop.targetProperty.SetValue(target, destinationClass);
}// See if there is a better way to do this.
else if (prop.targetProperty.PropertyType.GetInterfaces().Any(a => a.Name == "IEnumerable") && prop.sourceProperty.PropertyType.IsGenericType
&& prop.targetProperty.PropertyType.IsGenericType
&& sourceValue != null)
{
CopyToModelList(source, target, prop.targetProperty, prop.sourceProperty, sourceValue);
}
else
{
// CopySingleData(source, target, prop.targetProperty, prop.sourceProperty, targetValue, sourceValue);
prop.targetProperty.SetValue(target, prop.sourceProperty.GetValue(source, null), null);
}
}
}
}
// return target;
}
private static void CopyToModelList(object source, object target, PropertyInfo piTarget, PropertyInfo piSource, object sourceValue)
{
// int _sourceLength = (int)source.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, source, null);
// First create a generic collection that matches the piTarget being passed in.
var _listType = typeof(List<>);
props.sourceProperty.PropertyType.GetGenericArguments();
var _genericTargetArgs = piTarget.PropertyType.GetGenericArguments();
var _concreteTargetType = _listType.MakeGenericType(_genericTargetArgs);
var _newtargetList = Activator.CreateInstance(_concreteTargetType);
dynamic _sourceList = piSource.GetValue(source, null);
foreach (var _source in _sourceList)
{
object _tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetGenericArguments()[0]);
CopyToModelProperties(_source, _tempTarget);
// here we have to make recursive call to copy data and populate the target list.
//_targetList.Add(CopyObjectPropertyData(_source, (dynamic)new object()));
_newtargetList.GetType().GetMethod("Add").Invoke(_newtargetList, new[] { _tempTarget });
}
piTarget.SetValue(target, _newtargetList, null);
}
public static string GetModelPropertyNameFromSqlAttribute(Type model, string sqlName)
{
string _ret = "";
var _property = model.GetProperties().Where(w => w.GetCustomAttributes<SLSqlTableAttribute>().Where(w1 => w1.FieldName == sqlName).Any()).FirstOrDefault();
if(_property != null)
{
_ret = _property.Name;
}
return _ret;
}
Code is such a PITA to edit on this site, creating a new answer that includes caching. The original posted code performs horribly with 60 records to map. About 5 seconds. The new code with caching, 600 records .36 seconds. In this code we have audit fields and only our mapper can set or change Create audit fields. It may not apply to you and if so, remove. All the code below requires is a simple Sql Attribute such as below. Update that on your model properties if your sql table field names are different than your model property names. So much easier than Automapper because you don't other type of configuration and if your sql properties match your model properties, then you can use the below to create your own mapper method.
[AttributeUsage(AttributeTargets.Property)]
public class SLSqlTableAttribute : Attribute
{
protected string _fieldName { get; set; }
public string FieldName
{
get
{
return _fieldName;
}
set
{
_fieldName = value;
}
}
public SLSqlTableAttribute(string fieldName)
{
FieldName = fieldName;
}
}
public class SourceTargetProperties
{
public PropertyInfo SourceProperty { get; set; }
public PropertyInfo TargetProperty { get; set; }
}
public static class DataMapper
{
static Dictionary<string, List<SourceTargetProperties>> _dictTypeProperties = new Dictionary<string, List<SourceTargetProperties>>();
public static void CopyToSqlProperties(object source, object target, int? userId, DateTimeOffset? modifiedDateTime, bool isInsert)
{
Type _typeTarget = target.GetType();
Type _typeSrc = source.GetType();
// If we're passed in a collection
if (_typeSrc.GetInterfaces().Any(a => a.Name == "IEnumerable") && _typeSrc.IsGenericType && _typeTarget.IsGenericType)
{
dynamic _sourceList = source;
foreach (var _source in _sourceList)
{
object _tempTarget = Activator.CreateInstance(_typeTarget.GetGenericArguments()[0]);
CopyToModelProperties(_source, _tempTarget);
// here we have to make recursive call to copy data and populate the target list.
//_targetList.Add(CopyObjectPropertyData(_source, (dynamic)new object()));
// _newtargetList.GetType().GetMethod("Add").Invoke(_newtargetList, new[] { _tempTarget });
target.GetType().GetMethod("Add").Invoke(target, new[] { _tempTarget });
}
}
else
{
// Collect all the valid properties to map
if (!_dictTypeProperties.ContainsKey(_typeSrc.BaseType.Name))
{
_dictTypeProperties.Add(_typeSrc.BaseType.Name, (from srcProp in _typeSrc.GetProperties()
let targetProperty = _typeTarget.GetProperty(GetModelPropertyNameFromSqlAttribute(_typeTarget, srcProp.Name))
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
// && targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new SourceTargetProperties { SourceProperty = srcProp, TargetProperty = targetProperty }).ToList());
}
foreach (var prop in _dictTypeProperties[_typeSrc.BaseType.Name])
{
if (prop.TargetProperty.CanWrite && prop.SourceProperty.CanRead)
{
object targetValue = prop.TargetProperty.GetValue(target, null);
object sourceValue = prop.SourceProperty.GetValue(source, null);
if (sourceValue == null) { continue; }
//if (prop.sourceProperty.PropertyType.IsArray
// && prop.targetProperty.PropertyType.IsArray
// && sourceValue != null)
if (prop.SourceProperty.PropertyType.IsClass && prop.TargetProperty.PropertyType != typeof(string))
{
var destinationClass = Activator.CreateInstance(prop.TargetProperty.PropertyType);
object copyValue = prop.SourceProperty.GetValue(source);
CopyToModelProperties(copyValue, destinationClass);
prop.TargetProperty.SetValue(target, destinationClass);
}// See if there is a better way to do this.
else if (prop.TargetProperty.PropertyType.GetInterfaces().Any(a => a.Name == "IEnumerable") && prop.SourceProperty.PropertyType.IsGenericType
&& prop.TargetProperty.PropertyType.IsGenericType
&& sourceValue != null)
{
CopyToModelList(source, target, prop.TargetProperty, prop.SourceProperty, sourceValue);
}
else
{
string _targetPropertyName = prop.TargetProperty.Name;
if (modifiedDateTime.HasValue && (_targetPropertyName == "CreatedDateTime" || _targetPropertyName == "LastModifiedDateTime" || _targetPropertyName == "CreatedBy" ||
_targetPropertyName == "LastModifiedBy"))
{
switch (_targetPropertyName)
{
case "CreatedDateTime":
if (isInsert)
{
prop.TargetProperty.SetValue(target, modifiedDateTime, null);
}
break;
case "CreatedBy":
if (isInsert)
{
prop.TargetProperty.SetValue(target, userId, null);
}
break;
case "LastModifiedDateTime":
prop.TargetProperty.SetValue(target, modifiedDateTime, null);
break;
case "LastModifiedBy":
prop.TargetProperty.SetValue(target, userId, null);
break;
}
}
else
{
prop.TargetProperty.SetValue(target, prop.SourceProperty.GetValue(source, null), null);
}
}
}
}
}
}
/// <summary>
/// Copy from SQL EF Entities to our Models.
/// Models will have the sql table name as a SLSqlAttribute for this to work.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static void CopyToModelProperties(object source, object target)
{
Type _typeTarget = target.GetType();
Type _typeSrc = source.GetType();
if (_typeSrc.GetInterfaces().Any(a => a.Name == "IEnumerable") && _typeSrc.IsGenericType && _typeTarget.IsGenericType)
{
// figure out a way to take in collections here instead of having to loop through outside of code.
// int _sourceLength = (int)source.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, source, null);
var _listType = typeof(List<>);
//var _genericArgs = props.sourceProperty.PropertyType.GetGenericArguments();
//var _genericTargetArgs = _typeTarget.GetGenericArguments();
//var _concreteTargetType = _listType.MakeGenericType(_genericTargetArgs);
//var _newtargetList = Activator.CreateInstance(_concreteTargetType);
//_newtargetList = piSource.GetValue(source, null);
//var _genericTargetArgs
dynamic _sourceList = source;
//dynamic _sourceList = _typeSrc.GetValue(source, null);
// dynamic _targetList = piTarget.GetValue(target, null);
foreach (var _source in _sourceList)
{
object _tempTarget = Activator.CreateInstance(_typeTarget.GetGenericArguments()[0]);
CopyToModelProperties(_source, _tempTarget);
// here we have to make recursive call to copy data and populate the target list.
//_targetList.Add(CopyObjectPropertyData(_source, (dynamic)new object()));
// _newtargetList.GetType().GetMethod("Add").Invoke(_newtargetList, new[] { _tempTarget });
target.GetType().GetMethod("Add").Invoke(target, new[] { _tempTarget });
}
// _typeTarget.SetValue(target, _newtargetList, null);
}
else
{
// Collect all the valid properties to map
if (!_dictTypeProperties.ContainsKey(_typeSrc.BaseType.Name))
{
_dictTypeProperties.Add(_typeSrc.BaseType.Name, (from srcProp in _typeSrc.GetProperties()
let targetProperty = _typeTarget.GetProperty(GetModelPropertyNameFromSqlAttribute(_typeTarget, srcProp.Name))
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
// && targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new SourceTargetProperties { SourceProperty = srcProp, TargetProperty = targetProperty }).ToList());
}
foreach (var prop in _dictTypeProperties[_typeSrc.BaseType.Name])
{
if (prop.TargetProperty.CanWrite && prop.SourceProperty.CanRead)
{
object targetValue = prop.TargetProperty.GetValue(target, null);
object sourceValue = prop.SourceProperty.GetValue(source, null);
if (sourceValue == null) { continue; }
//if (prop.sourceProperty.PropertyType.IsArray
// && prop.targetProperty.PropertyType.IsArray
// && sourceValue != null)
if (prop.SourceProperty.PropertyType.IsClass && prop.TargetProperty.PropertyType != typeof(string))
{
var destinationClass = Activator.CreateInstance(prop.TargetProperty.PropertyType);
object copyValue = prop.SourceProperty.GetValue(source);
CopyToModelProperties(copyValue, destinationClass);
prop.TargetProperty.SetValue(target, destinationClass);
}// See if there is a better way to do this.
else if (prop.TargetProperty.PropertyType.GetInterfaces().Any(a => a.Name == "IEnumerable") && prop.SourceProperty.PropertyType.IsGenericType
&& prop.TargetProperty.PropertyType.IsGenericType
&& sourceValue != null)
{
CopyToModelList(source, target, prop.TargetProperty, prop.SourceProperty, sourceValue);
}
else
{
string _targetPropertyName = prop.TargetProperty.Name;
prop.TargetProperty.SetValue(target, prop.SourceProperty.GetValue(source, null), null);
}
}
}
}
// return target;
}
public static string GetModelPropertyNameFromSqlAttribute(Type model, string sqlName)
{
string _ret = "";
var _property = model.GetProperties().Where(w => w.GetCustomAttributes<SLSqlTableAttribute>().Where(w1 => w1.FieldName == sqlName).Any()).FirstOrDefault();
if (_property != null)
{
_ret = _property.Name;
}
return _ret;
}
private static void CopyToModelList(object source, object target, PropertyInfo piTarget, PropertyInfo piSource, object sourceValue)
{
// int _sourceLength = (int)source.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, source, null);
// First create a generic collection that matches the piTarget being passed in.
var _listType = typeof(List<>);
//var _genericArgs = props.sourceProperty.PropertyType.GetGenericArguments();
var _genericTargetArgs = piTarget.PropertyType.GetGenericArguments();
var _concreteTargetType = _listType.MakeGenericType(_genericTargetArgs);
var _newtargetList = Activator.CreateInstance(_concreteTargetType);
//_newtargetList = piSource.GetValue(source, null);
//var _genericTargetArgs
dynamic _sourceList = piSource.GetValue(source, null);
// dynamic _targetList = piTarget.GetValue(target, null);
foreach (var _source in _sourceList)
{
object _tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetGenericArguments()[0]);
CopyToModelProperties(_source, _tempTarget);
// here we have to make recursive call to copy data and populate the target list.
//_targetList.Add(CopyObjectPropertyData(_source, (dynamic)new object()));
_newtargetList.GetType().GetMethod("Add").Invoke(_newtargetList, new[] { _tempTarget });
}
piTarget.SetValue(target, _newtargetList, null);
}
}
I have two complex objects like Object1 and Object2. They have around 5 levels of child objects.
I need the fastest method to say if they are same or not.
How could this be done in C# 4.0?
Implement IEquatable<T> (typically in conjunction with overriding the inherited Object.Equals and Object.GetHashCode methods) on all your custom types. In the case of composite types, invoke the contained types’ Equals method within the containing types. For contained collections, use the SequenceEqual extension method, which internally calls IEquatable<T>.Equals or Object.Equals on each element. This approach will obviously require you to extend your types’ definitions, but its results are faster than any generic solutions involving serialization.
Edit: Here is a contrived example with three levels of nesting.
For value types, you can typically just call their Equals method. Even if the fields or properties were never explicitly assigned, they would still have a default value.
For reference types, you should first call ReferenceEquals, which checks for reference equality – this would serve as an efficiency boost when you happen to be referencing the same object. It would also handle cases where both references are null. If that check fails, confirm that your instance's field or property is not null (to avoid NullReferenceException) and call its Equals method. Since our members are properly typed, the IEquatable<T>.Equals method gets called directly, bypassing the overridden Object.Equals method (whose execution would be marginally slower due to the type cast).
When you override Object.Equals, you’re also expected to override Object.GetHashCode; I didn’t do so below for the sake of conciseness.
public class Person : IEquatable<Person>
{
public int Age { get; set; }
public string FirstName { get; set; }
public Address Address { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public bool Equals(Person other)
{
if (other == null)
return false;
return this.Age.Equals(other.Age) &&
(
object.ReferenceEquals(this.FirstName, other.FirstName) ||
this.FirstName != null &&
this.FirstName.Equals(other.FirstName)
) &&
(
object.ReferenceEquals(this.Address, other.Address) ||
this.Address != null &&
this.Address.Equals(other.Address)
);
}
}
public class Address : IEquatable<Address>
{
public int HouseNo { get; set; }
public string Street { get; set; }
public City City { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Address);
}
public bool Equals(Address other)
{
if (other == null)
return false;
return this.HouseNo.Equals(other.HouseNo) &&
(
object.ReferenceEquals(this.Street, other.Street) ||
this.Street != null &&
this.Street.Equals(other.Street)
) &&
(
object.ReferenceEquals(this.City, other.City) ||
this.City != null &&
this.City.Equals(other.City)
);
}
}
public class City : IEquatable<City>
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as City);
}
public bool Equals(City other)
{
if (other == null)
return false;
return
object.ReferenceEquals(this.Name, other.Name) ||
this.Name != null &&
this.Name.Equals(other.Name);
}
}
Update: This answer was written several years ago. Since then, I've started to lean away from implementing IEquality<T> for mutable types for such scenarios. There are two notions of equality: identity and equivalence. At a memory representation level, these are popularly distinguished as “reference equality” and “value equality” (see Equality Comparisons). However, the same distinction can also apply at a domain level. Suppose that your Person class has a PersonId property, unique per distinct real-world person. Should two objects with the same PersonId but different Age values be considered equal or different? The answer above assumes that one is after equivalence. However, there are many usages of the IEquality<T> interface, such as collections, that assume that such implementations provide for identity. For example, if you're populating a HashSet<T>, you would typically expect a TryGetValue(T,T) call to return existing elements that share merely the identity of your argument, not necessarily equivalent elements whose contents are completely the same. This notion is enforced by the notes on GetHashCode:
In general, for mutable reference types, you should override GetHashCode() only if:
You can compute the hash code from fields that are not mutable; or
You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code.
Serialize both objects and compare the resulting strings
You can use extension method, recursion to resolve this problem:
public static bool DeepCompare(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
//Compare two object's class, return false if they are difference
if (obj.GetType() != another.GetType()) return false;
var result = true;
//Get all properties of obj
//And compare each other
foreach (var property in obj.GetType().GetProperties())
{
var objValue = property.GetValue(obj);
var anotherValue = property.GetValue(another);
if (!objValue.Equals(anotherValue)) result = false;
}
return result;
}
public static bool CompareEx(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
//properties: int, double, DateTime, etc, not class
if (!obj.GetType().IsClass) return obj.Equals(another);
var result = true;
foreach (var property in obj.GetType().GetProperties())
{
var objValue = property.GetValue(obj);
var anotherValue = property.GetValue(another);
//Recursion
if (!objValue.DeepCompare(anotherValue)) result = false;
}
return result;
}
or compare by using Json (if object is very complex)
You can use Newtonsoft.Json:
public static bool JsonCompare(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
var objJson = JsonConvert.SerializeObject(obj);
var anotherJson = JsonConvert.SerializeObject(another);
return objJson == anotherJson;
}
If you don't want to implement IEquatable, you can always use Reflection to compare all the properties:
- if they're value type, just compare them
-if they are reference type, call the function recursively to compare its "inner" properties.
I'm not thinking about performace, but about simplicity. It depends, however on the exact design of your objects. It could get complicated depending on your objects shape (for example if there are cyclic dependencies between properties). There are, however, several solutions out there that you can use, like this one:
Compare .NET objects
Another option is to serialize the object as text, for example using JSON.NET, and comparing the serialization result. (JSON.NET can handle Cyclic dependencies between properties).
I don't know if by fastest you mean the fastest way to implement it or a code that runs fast. You should not optimize before knowing if you need to. Premature optimization is the root of all evil
Serialize both objects and compare the resulting strings by #JoelFan
So to do this, create a static class like so and use Extensions to extend ALL objects (so you can pass anytype of object, collection, etc into the method)
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
public static class MySerializer
{
public static string Serialize(this object obj)
{
var serializer = new DataContractJsonSerializer(obj.GetType());
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.Default.GetString(ms.ToArray());
}
}
}
Once you reference this static class in any other file, you can do this:
Person p = new Person { Firstname = "Jason", LastName = "Argonauts" };
Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" };
//assuming you have already created a class person!
string personString = p.Serialize();
string person2String = p2.Serialize();
Now you can simply use .Equals to compare them.
I use this for checking if objects are in collections too. It works really well.
If you have a requirement where you want the class which is immutable. I mean that none of the properties can be modified once it's been created. In that case, C# 9 have a feature which is called a record.
You can easily compare records by values and types is they are equal.
public record Person
{
public string LastName { get; }
public string FirstName { get; }
public Person(string first, string last) => (FirstName, LastName) = (first, last);
}
var person1 = new Person("Bill", "Wagner");
var person2 = new Person("Bill", "Wagner");
Console.WriteLine(person1 == person2); // true
You can now use json.net. Just go on Nuget and install it.
And you can do something like this:
public bool Equals(SamplesItem sampleToCompare)
{
string myself = JsonConvert.SerializeObject(this);
string other = JsonConvert.SerializeObject(sampleToCompare);
return myself == other;
}
You could perhaps make a extension method for object if you wanted to get fancier. Please note this only compares the public properties. And if you wanted to ignore a public property when you do the comparison you could use the [JsonIgnore] attribute.
Serialize both objects, then calculate Hash Code, then compare.
I'll assume you are not referring to literally the same objects
Object1 == Object2
You might be thinking about doing a memory comparison between the two
memcmp(Object1, Object2, sizeof(Object.GetType())
But that's not even real code in c# :). Because all of your data is probably created on the heap, the memory is not contiguous and you can't just compare the equality of two objects in an agnostic manner. You're going to have to compare each value, one at a time, in a custom way.
Consider adding the IEquatable<T> interface to your class, and define a custom Equals method for your type. Then, in that method, manual test each value. Add IEquatable<T> again on enclosed types if you can and repeat the process.
class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
/* check all the values */
return false;
}
}
Based off a few answers already given here I decided to mostly back JoelFan's answer. I love extension methods and these have been working great for me when none of the other solutions would using them to compare my complex classes.
Extension Methods
using System.IO;
using System.Xml.Serialization;
static class ObjectHelpers
{
public static string SerializeObject<T>(this T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
public static bool EqualTo(this object obj, object toCompare)
{
if (obj.SerializeObject() == toCompare.SerializeObject())
return true;
else
return false;
}
public static bool IsBlank<T>(this T obj) where T: new()
{
T blank = new T();
T newObj = ((T)obj);
if (newObj.SerializeObject() == blank.SerializeObject())
return true;
else
return false;
}
}
Usage Examples
if (record.IsBlank())
throw new Exception("Record found is blank.");
if (record.EqualTo(new record()))
throw new Exception("Record found is blank.");
I would say that:
Object1.Equals(Object2)
would be what you're looking for. That's if you're looking to see if the objects are the same, which is what you seem to be asking.
If you want to check to see if all the child objects are the same, run them through a loop with the Equals() method.
I found this below function for comparing objects.
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 (object.Equals(Object1, default(T)) || object.Equals(object2, default(T)))
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;
}
I am using it and it is working fine for me.
Thanks to the example of Jonathan. I expanded it for all cases (arrays, lists, dictionaries, primitive types).
This is a comparison without serialization and does not require the implementation of any interfaces for compared objects.
/// <summary>Returns description of difference or empty value if equal</summary>
public static string Compare(object obj1, object obj2, string path = "")
{
string path1 = string.IsNullOrEmpty(path) ? "" : path + ": ";
if (obj1 == null && obj2 != null)
return path1 + "null != not null";
else if (obj2 == null && obj1 != null)
return path1 + "not null != null";
else if (obj1 == null && obj2 == null)
return null;
if (!obj1.GetType().Equals(obj2.GetType()))
return "different types: " + obj1.GetType() + " and " + obj2.GetType();
Type type = obj1.GetType();
if (path == "")
path = type.Name;
if (type.IsPrimitive || typeof(string).Equals(type))
{
if (!obj1.Equals(obj2))
return path1 + "'" + obj1 + "' != '" + obj2 + "'";
return null;
}
if (type.IsArray)
{
Array first = obj1 as Array;
Array second = obj2 as Array;
if (first.Length != second.Length)
return path1 + "array size differs (" + first.Length + " vs " + second.Length + ")";
var en = first.GetEnumerator();
int i = 0;
while (en.MoveNext())
{
string res = Compare(en.Current, second.GetValue(i), path);
if (res != null)
return res + " (Index " + i + ")";
i++;
}
}
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type))
{
System.Collections.IEnumerable first = obj1 as System.Collections.IEnumerable;
System.Collections.IEnumerable second = obj2 as System.Collections.IEnumerable;
var en = first.GetEnumerator();
var en2 = second.GetEnumerator();
int i = 0;
while (en.MoveNext())
{
if (!en2.MoveNext())
return path + ": enumerable size differs";
string res = Compare(en.Current, en2.Current, path);
if (res != null)
return res + " (Index " + i + ")";
i++;
}
}
else
{
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
{
try
{
var val = pi.GetValue(obj1);
var tval = pi.GetValue(obj2);
if (path.EndsWith("." + pi.Name))
return null;
var pathNew = (path.Length == 0 ? "" : path + ".") + pi.Name;
string res = Compare(val, tval, pathNew);
if (res != null)
return res;
}
catch (TargetParameterCountException)
{
//index property
}
}
foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
{
var val = fi.GetValue(obj1);
var tval = fi.GetValue(obj2);
if (path.EndsWith("." + fi.Name))
return null;
var pathNew = (path.Length == 0 ? "" : path + ".") + fi.Name;
string res = Compare(val, tval, pathNew);
if (res != null)
return res;
}
}
return null;
}
For easy copying of the code created repository
public class GetObjectsComparison
{
public object FirstObject, SecondObject;
public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
}
public struct SetObjectsComparison
{
public FieldInfo SecondObjectFieldInfo;
public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue;
public bool ErrorFound;
public GetObjectsComparison GetObjectsComparison;
}
private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison)
{
GetObjectsComparison FunctionGet = GetObjectsComparison;
SetObjectsComparison FunctionSet = new SetObjectsComparison();
if (FunctionSet.ErrorFound==false)
foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions))
{
FunctionSet.SecondObjectFieldInfo =
FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions);
FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject);
FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject);
if (FirstObjectFieldInfo.FieldType.IsNested)
{
FunctionSet.GetObjectsComparison =
new GetObjectsComparison()
{
FirstObject = FunctionSet.FirstObjectFieldInfoValue
,
SecondObject = FunctionSet.SecondObjectFieldInfoValue
};
if (!ObjectsComparison(FunctionSet.GetObjectsComparison))
{
FunctionSet.ErrorFound = true;
break;
}
}
else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue)
{
FunctionSet.ErrorFound = true;
break;
}
}
return !FunctionSet.ErrorFound;
}
Generic Extension Method
public static class GenericExtensions
{
public static bool DeepCompare<T>(this T objA, T objB)
{
if (typeof(T).IsValueType)
return objA.Equals(objB);
if (ReferenceEquals(objA, objB))
return true;
if ((objA == null) || (objB == null))
return false;
if (typeof(T) is IEnumerable)
{
var enumerableA = (IEnumerable<T>) objA;
var enumerableB = (IEnumerable<T>) objB;
if (enumerableA.Count() != enumerableB.Count())
return false;
using (var enumeratorA = enumerableA.GetEnumerator())
using (var enumeratorB = enumerableB.GetEnumerator())
{
while (true)
{
bool moveNextA = enumeratorA.MoveNext();
bool moveNextB = enumeratorB.MoveNext();
if (!moveNextA || !moveNextB)
break;
var currentA = enumeratorA.Current;
var currentB = enumeratorB.Current;
if (!currentA.DeepCompare<T>(currentB))
return false;
}
return true;
}
}
foreach (var property in objA.GetType().GetProperties())
{
var valueA = property.GetValue(objA);
var valueB = property.GetValue(objB);
if (!valueA.DeepCompare(valueB))
return false;
}
return true;
}
}
One way to do this would be to override Equals() on each type involved. For example, your top level object would override Equals() to call the Equals() method of all 5 child objects. Those objects should all override Equals() as well, assuming they are custom objects, and so on until the entire hierarchy could be compared by just performing an equality check on the top level objects.
Use IEquatable<T> Interface which has a method Equals.
To return each property updated:
public IEnumerable<string> GetPropsUpdated(T oldModel, T newModel)
{
var diff = new List<string>();
foreach (var prop in oldModel.GetType().GetProperties())
{
var oldValue = prop.GetValue(oldModel);
var newValue = prop.GetValue(newModel);
if (oldValue == null && newValue == null)
continue;
if (oldValue == null && newValue != null
|| oldValue != null && newValue == null)
{
diff.Add(prop.Name);
continue;
}
var oldPropHashed = oldValue.GetHashCode();
var newPropHashed = newValue.GetHashCode();
if (!oldPropHashed.Equals(newPropHashed))
diff.Add(prop.Name);
}
return diff;
}
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");
I'd like to compare the data types of two classes and return bool values. The problem is my method doesn't compare values inside class of a class
Here is the code:
public static class Compare
{
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)
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;
}
}
And here is the comparison:
private void Form1_Load(object sender, EventArgs e)
{
Obj1 obj1 = new Obj1();
Obj1 obj11 = new Obj1();
Obj2 obj2 = new Obj2();
Obj2 obj22 = new Obj2();
obj1.param1 = "1";
obj1.param2 = "2";
obj2.param3 = "3";
obj1.obj2 = obj2;
obj11.param1 = "1";
obj11.param2 = "2";
obj22.param3 = "3";
obj11.obj2 = obj22;
bool res = Compare.PublicInstancePropertiesEqual(obj1, obj11, ("secure"));
}
}
class Obj1
{
public string param1 { get; set; }
public string param2 { get; set; }
public Obj2 obj2 { get; set; }
}
class Obj2
{
public string param3 { get; set; }
public decimal param4 { get; set; }
}
The returned value is false by res
When you come to compare the two instances of Obj2 they will only be equal if they are the same object.
To perform structural equality you need to recurse through all reference types (ie. classes), just directly comparing value types (ie. structs, which use structural equality by default). Note int etc. are value types.
I would suggest checking for types that overrides Equals, implement IEquatable<T>, IComparable<T> etc.: all the indications that a type has its own definition of equal.
In your code, the values of obj1.obj2 and obj11.obj2 are different and the comparison method uses Object.Equals to compare members of the classes, which is why the Compare.PublicInstancePropertiesEqual method returns false.
I.e.: obj1.obj2 = obj2; but obj11.obj2 = obj22;
If you would like to compare values recursively, you should replace the line
where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
with
where selfValue != toValue && (selfValue == null || !PublicInstancePropertiesEqual(selfValue, toValue, ignore))
I have two complex objects like Object1 and Object2. They have around 5 levels of child objects.
I need the fastest method to say if they are same or not.
How could this be done in C# 4.0?
Implement IEquatable<T> (typically in conjunction with overriding the inherited Object.Equals and Object.GetHashCode methods) on all your custom types. In the case of composite types, invoke the contained types’ Equals method within the containing types. For contained collections, use the SequenceEqual extension method, which internally calls IEquatable<T>.Equals or Object.Equals on each element. This approach will obviously require you to extend your types’ definitions, but its results are faster than any generic solutions involving serialization.
Edit: Here is a contrived example with three levels of nesting.
For value types, you can typically just call their Equals method. Even if the fields or properties were never explicitly assigned, they would still have a default value.
For reference types, you should first call ReferenceEquals, which checks for reference equality – this would serve as an efficiency boost when you happen to be referencing the same object. It would also handle cases where both references are null. If that check fails, confirm that your instance's field or property is not null (to avoid NullReferenceException) and call its Equals method. Since our members are properly typed, the IEquatable<T>.Equals method gets called directly, bypassing the overridden Object.Equals method (whose execution would be marginally slower due to the type cast).
When you override Object.Equals, you’re also expected to override Object.GetHashCode; I didn’t do so below for the sake of conciseness.
public class Person : IEquatable<Person>
{
public int Age { get; set; }
public string FirstName { get; set; }
public Address Address { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public bool Equals(Person other)
{
if (other == null)
return false;
return this.Age.Equals(other.Age) &&
(
object.ReferenceEquals(this.FirstName, other.FirstName) ||
this.FirstName != null &&
this.FirstName.Equals(other.FirstName)
) &&
(
object.ReferenceEquals(this.Address, other.Address) ||
this.Address != null &&
this.Address.Equals(other.Address)
);
}
}
public class Address : IEquatable<Address>
{
public int HouseNo { get; set; }
public string Street { get; set; }
public City City { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Address);
}
public bool Equals(Address other)
{
if (other == null)
return false;
return this.HouseNo.Equals(other.HouseNo) &&
(
object.ReferenceEquals(this.Street, other.Street) ||
this.Street != null &&
this.Street.Equals(other.Street)
) &&
(
object.ReferenceEquals(this.City, other.City) ||
this.City != null &&
this.City.Equals(other.City)
);
}
}
public class City : IEquatable<City>
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as City);
}
public bool Equals(City other)
{
if (other == null)
return false;
return
object.ReferenceEquals(this.Name, other.Name) ||
this.Name != null &&
this.Name.Equals(other.Name);
}
}
Update: This answer was written several years ago. Since then, I've started to lean away from implementing IEquality<T> for mutable types for such scenarios. There are two notions of equality: identity and equivalence. At a memory representation level, these are popularly distinguished as “reference equality” and “value equality” (see Equality Comparisons). However, the same distinction can also apply at a domain level. Suppose that your Person class has a PersonId property, unique per distinct real-world person. Should two objects with the same PersonId but different Age values be considered equal or different? The answer above assumes that one is after equivalence. However, there are many usages of the IEquality<T> interface, such as collections, that assume that such implementations provide for identity. For example, if you're populating a HashSet<T>, you would typically expect a TryGetValue(T,T) call to return existing elements that share merely the identity of your argument, not necessarily equivalent elements whose contents are completely the same. This notion is enforced by the notes on GetHashCode:
In general, for mutable reference types, you should override GetHashCode() only if:
You can compute the hash code from fields that are not mutable; or
You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code.
Serialize both objects and compare the resulting strings
You can use extension method, recursion to resolve this problem:
public static bool DeepCompare(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
//Compare two object's class, return false if they are difference
if (obj.GetType() != another.GetType()) return false;
var result = true;
//Get all properties of obj
//And compare each other
foreach (var property in obj.GetType().GetProperties())
{
var objValue = property.GetValue(obj);
var anotherValue = property.GetValue(another);
if (!objValue.Equals(anotherValue)) result = false;
}
return result;
}
public static bool CompareEx(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
//properties: int, double, DateTime, etc, not class
if (!obj.GetType().IsClass) return obj.Equals(another);
var result = true;
foreach (var property in obj.GetType().GetProperties())
{
var objValue = property.GetValue(obj);
var anotherValue = property.GetValue(another);
//Recursion
if (!objValue.DeepCompare(anotherValue)) result = false;
}
return result;
}
or compare by using Json (if object is very complex)
You can use Newtonsoft.Json:
public static bool JsonCompare(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
var objJson = JsonConvert.SerializeObject(obj);
var anotherJson = JsonConvert.SerializeObject(another);
return objJson == anotherJson;
}
If you don't want to implement IEquatable, you can always use Reflection to compare all the properties:
- if they're value type, just compare them
-if they are reference type, call the function recursively to compare its "inner" properties.
I'm not thinking about performace, but about simplicity. It depends, however on the exact design of your objects. It could get complicated depending on your objects shape (for example if there are cyclic dependencies between properties). There are, however, several solutions out there that you can use, like this one:
Compare .NET objects
Another option is to serialize the object as text, for example using JSON.NET, and comparing the serialization result. (JSON.NET can handle Cyclic dependencies between properties).
I don't know if by fastest you mean the fastest way to implement it or a code that runs fast. You should not optimize before knowing if you need to. Premature optimization is the root of all evil
Serialize both objects and compare the resulting strings by #JoelFan
So to do this, create a static class like so and use Extensions to extend ALL objects (so you can pass anytype of object, collection, etc into the method)
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
public static class MySerializer
{
public static string Serialize(this object obj)
{
var serializer = new DataContractJsonSerializer(obj.GetType());
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.Default.GetString(ms.ToArray());
}
}
}
Once you reference this static class in any other file, you can do this:
Person p = new Person { Firstname = "Jason", LastName = "Argonauts" };
Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" };
//assuming you have already created a class person!
string personString = p.Serialize();
string person2String = p2.Serialize();
Now you can simply use .Equals to compare them.
I use this for checking if objects are in collections too. It works really well.
If you have a requirement where you want the class which is immutable. I mean that none of the properties can be modified once it's been created. In that case, C# 9 have a feature which is called a record.
You can easily compare records by values and types is they are equal.
public record Person
{
public string LastName { get; }
public string FirstName { get; }
public Person(string first, string last) => (FirstName, LastName) = (first, last);
}
var person1 = new Person("Bill", "Wagner");
var person2 = new Person("Bill", "Wagner");
Console.WriteLine(person1 == person2); // true
You can now use json.net. Just go on Nuget and install it.
And you can do something like this:
public bool Equals(SamplesItem sampleToCompare)
{
string myself = JsonConvert.SerializeObject(this);
string other = JsonConvert.SerializeObject(sampleToCompare);
return myself == other;
}
You could perhaps make a extension method for object if you wanted to get fancier. Please note this only compares the public properties. And if you wanted to ignore a public property when you do the comparison you could use the [JsonIgnore] attribute.
Serialize both objects, then calculate Hash Code, then compare.
I'll assume you are not referring to literally the same objects
Object1 == Object2
You might be thinking about doing a memory comparison between the two
memcmp(Object1, Object2, sizeof(Object.GetType())
But that's not even real code in c# :). Because all of your data is probably created on the heap, the memory is not contiguous and you can't just compare the equality of two objects in an agnostic manner. You're going to have to compare each value, one at a time, in a custom way.
Consider adding the IEquatable<T> interface to your class, and define a custom Equals method for your type. Then, in that method, manual test each value. Add IEquatable<T> again on enclosed types if you can and repeat the process.
class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
/* check all the values */
return false;
}
}
Based off a few answers already given here I decided to mostly back JoelFan's answer. I love extension methods and these have been working great for me when none of the other solutions would using them to compare my complex classes.
Extension Methods
using System.IO;
using System.Xml.Serialization;
static class ObjectHelpers
{
public static string SerializeObject<T>(this T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
public static bool EqualTo(this object obj, object toCompare)
{
if (obj.SerializeObject() == toCompare.SerializeObject())
return true;
else
return false;
}
public static bool IsBlank<T>(this T obj) where T: new()
{
T blank = new T();
T newObj = ((T)obj);
if (newObj.SerializeObject() == blank.SerializeObject())
return true;
else
return false;
}
}
Usage Examples
if (record.IsBlank())
throw new Exception("Record found is blank.");
if (record.EqualTo(new record()))
throw new Exception("Record found is blank.");
I would say that:
Object1.Equals(Object2)
would be what you're looking for. That's if you're looking to see if the objects are the same, which is what you seem to be asking.
If you want to check to see if all the child objects are the same, run them through a loop with the Equals() method.
I found this below function for comparing objects.
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 (object.Equals(Object1, default(T)) || object.Equals(object2, default(T)))
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;
}
I am using it and it is working fine for me.
Thanks to the example of Jonathan. I expanded it for all cases (arrays, lists, dictionaries, primitive types).
This is a comparison without serialization and does not require the implementation of any interfaces for compared objects.
/// <summary>Returns description of difference or empty value if equal</summary>
public static string Compare(object obj1, object obj2, string path = "")
{
string path1 = string.IsNullOrEmpty(path) ? "" : path + ": ";
if (obj1 == null && obj2 != null)
return path1 + "null != not null";
else if (obj2 == null && obj1 != null)
return path1 + "not null != null";
else if (obj1 == null && obj2 == null)
return null;
if (!obj1.GetType().Equals(obj2.GetType()))
return "different types: " + obj1.GetType() + " and " + obj2.GetType();
Type type = obj1.GetType();
if (path == "")
path = type.Name;
if (type.IsPrimitive || typeof(string).Equals(type))
{
if (!obj1.Equals(obj2))
return path1 + "'" + obj1 + "' != '" + obj2 + "'";
return null;
}
if (type.IsArray)
{
Array first = obj1 as Array;
Array second = obj2 as Array;
if (first.Length != second.Length)
return path1 + "array size differs (" + first.Length + " vs " + second.Length + ")";
var en = first.GetEnumerator();
int i = 0;
while (en.MoveNext())
{
string res = Compare(en.Current, second.GetValue(i), path);
if (res != null)
return res + " (Index " + i + ")";
i++;
}
}
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type))
{
System.Collections.IEnumerable first = obj1 as System.Collections.IEnumerable;
System.Collections.IEnumerable second = obj2 as System.Collections.IEnumerable;
var en = first.GetEnumerator();
var en2 = second.GetEnumerator();
int i = 0;
while (en.MoveNext())
{
if (!en2.MoveNext())
return path + ": enumerable size differs";
string res = Compare(en.Current, en2.Current, path);
if (res != null)
return res + " (Index " + i + ")";
i++;
}
}
else
{
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
{
try
{
var val = pi.GetValue(obj1);
var tval = pi.GetValue(obj2);
if (path.EndsWith("." + pi.Name))
return null;
var pathNew = (path.Length == 0 ? "" : path + ".") + pi.Name;
string res = Compare(val, tval, pathNew);
if (res != null)
return res;
}
catch (TargetParameterCountException)
{
//index property
}
}
foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
{
var val = fi.GetValue(obj1);
var tval = fi.GetValue(obj2);
if (path.EndsWith("." + fi.Name))
return null;
var pathNew = (path.Length == 0 ? "" : path + ".") + fi.Name;
string res = Compare(val, tval, pathNew);
if (res != null)
return res;
}
}
return null;
}
For easy copying of the code created repository
public class GetObjectsComparison
{
public object FirstObject, SecondObject;
public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
}
public struct SetObjectsComparison
{
public FieldInfo SecondObjectFieldInfo;
public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue;
public bool ErrorFound;
public GetObjectsComparison GetObjectsComparison;
}
private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison)
{
GetObjectsComparison FunctionGet = GetObjectsComparison;
SetObjectsComparison FunctionSet = new SetObjectsComparison();
if (FunctionSet.ErrorFound==false)
foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions))
{
FunctionSet.SecondObjectFieldInfo =
FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions);
FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject);
FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject);
if (FirstObjectFieldInfo.FieldType.IsNested)
{
FunctionSet.GetObjectsComparison =
new GetObjectsComparison()
{
FirstObject = FunctionSet.FirstObjectFieldInfoValue
,
SecondObject = FunctionSet.SecondObjectFieldInfoValue
};
if (!ObjectsComparison(FunctionSet.GetObjectsComparison))
{
FunctionSet.ErrorFound = true;
break;
}
}
else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue)
{
FunctionSet.ErrorFound = true;
break;
}
}
return !FunctionSet.ErrorFound;
}
Generic Extension Method
public static class GenericExtensions
{
public static bool DeepCompare<T>(this T objA, T objB)
{
if (typeof(T).IsValueType)
return objA.Equals(objB);
if (ReferenceEquals(objA, objB))
return true;
if ((objA == null) || (objB == null))
return false;
if (typeof(T) is IEnumerable)
{
var enumerableA = (IEnumerable<T>) objA;
var enumerableB = (IEnumerable<T>) objB;
if (enumerableA.Count() != enumerableB.Count())
return false;
using (var enumeratorA = enumerableA.GetEnumerator())
using (var enumeratorB = enumerableB.GetEnumerator())
{
while (true)
{
bool moveNextA = enumeratorA.MoveNext();
bool moveNextB = enumeratorB.MoveNext();
if (!moveNextA || !moveNextB)
break;
var currentA = enumeratorA.Current;
var currentB = enumeratorB.Current;
if (!currentA.DeepCompare<T>(currentB))
return false;
}
return true;
}
}
foreach (var property in objA.GetType().GetProperties())
{
var valueA = property.GetValue(objA);
var valueB = property.GetValue(objB);
if (!valueA.DeepCompare(valueB))
return false;
}
return true;
}
}
One way to do this would be to override Equals() on each type involved. For example, your top level object would override Equals() to call the Equals() method of all 5 child objects. Those objects should all override Equals() as well, assuming they are custom objects, and so on until the entire hierarchy could be compared by just performing an equality check on the top level objects.
Use IEquatable<T> Interface which has a method Equals.
To return each property updated:
public IEnumerable<string> GetPropsUpdated(T oldModel, T newModel)
{
var diff = new List<string>();
foreach (var prop in oldModel.GetType().GetProperties())
{
var oldValue = prop.GetValue(oldModel);
var newValue = prop.GetValue(newModel);
if (oldValue == null && newValue == null)
continue;
if (oldValue == null && newValue != null
|| oldValue != null && newValue == null)
{
diff.Add(prop.Name);
continue;
}
var oldPropHashed = oldValue.GetHashCode();
var newPropHashed = newValue.GetHashCode();
if (!oldPropHashed.Equals(newPropHashed))
diff.Add(prop.Name);
}
return diff;
}