I have two complex (i.e. objects with string, int, double, List and other home made data type) objects of the same type. I would like to compare the content of both of them to ensure that they are identical. Note: The object doesn't implement .Equals (I have no control on that) and doesn't implement IComparable.
Is there a generic way (reflection?) to compare the content of the two objects?
Thanks!
I have created a class to perform a deep compare of .NET Objects. See:
https://github.com/GregFinzer/Compare-Net-Objects
My Working solution.!
private bool Compare(object obj1, object obj2)
{
if (obj1 == null || obj2 == null)
{
return false;
}
if (!obj1.GetType().Equals(obj2.GetType()))
{
return false;
}
Type type = obj1.GetType();
if (type.IsPrimitive || typeof(string).Equals(type))
{
return obj1.Equals(obj2);
}
if (type.IsArray)
{
Array first = obj1 as Array;
Array second = obj2 as Array;
var en = first.GetEnumerator();
int i = 0;
while (en.MoveNext())
{
if (!Compare(en.Current, second.GetValue(i)))
return false;
i++;
}
}
else
{
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
{
var val = pi.GetValue(obj1);
var tval = pi.GetValue(obj2);
if (!Compare(val, tval))
return false;
}
foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
{
var val = fi.GetValue(obj1);
var tval = fi.GetValue(obj2);
if (!Compare(val, tval))
return false;
}
}
return true;
}
Hope it helps.!
I just wrote my version. This function use generic and reflection. It works by recursive it self until all of things in the object already compare or found one that do not equal.
public class Utils
{
public bool CompareObjects<T>(T expectInput, T actualInput)
{
// If T is primitive type.
if (typeof(T).IsPrimitive)
{
if (expectInput.Equals(actualInput))
{
return true;
}
return false;
}
if (expectInput is IEquatable<T>)
{
if (expectInput.Equals(actualInput))
{
return true;
}
return false;
}
if (expectInput is IComparable)
{
if (((IComparable)expectInput).CompareTo(actualInput) == 0)
{
return true;
}
return false;
}
// If T is implement IEnumerable.
if (expectInput is IEnumerable)
{
var expectEnumerator = ((IEnumerable)expectInput).GetEnumerator();
var actualEnumerator = ((IEnumerable)actualInput).GetEnumerator();
var canGetExpectMember = expectEnumerator.MoveNext();
var canGetActualMember = actualEnumerator.MoveNext();
while (canGetExpectMember && canGetActualMember && true)
{
var currentType = expectEnumerator.Current.GetType();
object isEqual = typeof(Utils).GetMethod("CompareObjects").MakeGenericMethod(currentType).Invoke(null, new object[] { expectEnumerator.Current, actualEnumerator.Current });
if ((bool)isEqual == false)
{
return false;
}
canGetExpectMember = expectEnumerator.MoveNext();
canGetActualMember = actualEnumerator.MoveNext();
}
if (canGetExpectMember != canGetActualMember)
{
return false;
}
return true;
}
// If T is class.
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
var expectValue = typeof(T).GetProperty(property.Name).GetValue(expectInput);
var actualValue = typeof(T).GetProperty(property.Name).GetValue(actualInput);
if (expectValue == null || actualValue == null)
{
if (expectValue == null && actualValue == null)
{
continue;
}
return false;
}
object isEqual = typeof(Utils).GetMethod("CompareObjects").MakeGenericMethod(property.PropertyType).Invoke(null, new object[] { expectValue, actualValue });
if ((bool)isEqual == false)
{
return false;
}
}
return true;
}
}
Is there a generic way to compare the content of the two objects?
Well yes, but generally that's known as the IComparable interface.
If you could descend from the class and create a child that implemented IComparable, that might be ideal.
Reflection would be it, but the issue is the contained types - for example, you can't just use Equals or EqualityComparer<T>, since the sub-data also won't be conveniently comparable if it is a List<T> etc.
How often do you need to do this? Could you serialize them and compare the serialized value? That might be the most robust option.
GetHashcode works for me.
I override GetHashcode() in every class with all public relevant properties X-OR-ed
e.g.
override GetHashCode()
{
return A.GetHashCode() ^ B.GetHashCode ^ C.SafeString().Get..
}
I iterate this through all classes, again X-OR the values.
IsModified compares a previously HashValue with the current.
Two different objects could indeed return the same HashValue, with a chance of 1 to 4 billion, but for many purposes this is good enough for me.
But I have an even better idea, using a MemoryStream
here is an Extension:
public static bool IsBinaryEqualTo(this object obj, object obj1)
{
using (MemoryStream memStream = new MemoryStream())
{
if (obj == null || obj1 == null)
{
if (obj == null && obj1 == null)
return true;
else
return false;
}
BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
binaryFormatter.Serialize(memStream, obj);
byte[] b1 = memStream.ToArray();
memStream.SetLength(0);
binaryFormatter.Serialize(memStream, obj1);
byte[] b2 = memStream.ToArray();
if (b1.Length != b2.Length)
return false;
for (int i = 0; i < b1.Length; i++)
{
if (b1[i] != b2[i])
return false;
}
return true;
}
}
The fastest and easiest way I've found is to serialize both objects using MessagePack then compare the byte arrays.
public static bool DeepEquals(object o1, object o2)
{
var b1 = MessagePackSerializer.Serialize(o1, ContractlessStandardResolver.Instance);
var b2 = MessagePackSerializer.Serialize(o2, ContractlessStandardResolver.Instance);
return b1.SequenceEqual(b2);
}
Serialize the objects to XML string and you can end up doing a string comparison between the 2 objects that are serialized...
private string Serialize<T>(T value)
{
if (value == null)
{
return string.Empty;
}
try
{
XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
StringWriter stringWriter = new StringWriter();
XmlWriter writer = XmlWriter.Create(stringWriter);
xmlserializer.Serialize(writer, value);
string serializeXml = stringWriter.ToString();
writer.Close();
return serializeXml;
}
catch (Exception ex)
{
return string.Empty;
}
}
}
For my last project I made a nice deep compair with some features.
public class ObjektHelper
{
/// <summary>
/// Compairs two Objects and gives back true if they are equal
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj1">Object 1</param>
/// <param name="obj2">Object 2</param>
/// <param name="consideredFieldNames">If the list is not mepty, only the field within equal names are compaired.</param>
/// <param name="notConsideredFieldNames">If you want not compair some fields enter their name in this list.</param>
/// <returns></returns>
public static bool DeepCompare<T>(T obj1, T obj2, string[] consideredFieldNames, params string[] notConsideredFieldNames)
{
var errorList = new List<object>();
if (notConsideredFieldNames == null) notConsideredFieldNames = new[] {""};
DeepCompare(obj1, obj2, errorList, consideredFieldNames, notConsideredFieldNames, false);
return errorList.Count <= 0;
}
/// <summary>
/// Compairs two Objects and gives an error list back.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj1"></param>
/// <param name="obj2"></param>
/// <param name="errorList">The error list gives back the names of the fields that are not equal.</param>
/// <param name="consideredFieldNames">If the list is not mepty, only the field within equal names are compaired.</param>
/// <param name="notConsideredFieldNames">If you want not compair some fields enter their name in this list.</param>
/// <param name="endWithErrors">If the value is false, the method end at the first error.</param>
public static void DeepCompare<T>(T obj1, T obj2, List<object> errorList, string[] consideredFieldNames, string[] notConsideredFieldNames, bool endWithErrors)
{
if (errorList == null) throw new Exception("errorListliste ist NULL");
if (Equals(obj1, default(T)) && Equals(obj2, default(T))) return;
if (Equals(obj1, default(T)) || Equals(obj2, default(T)))
{
errorList.Add("One of the object are null!");
return;
}
if (!endWithErrors && errorList != null && errorList.Count > 0) throw new Exception("ErrorListliste is not empty");
var type1 = obj1.GetType();
var type2 = obj2.GetType();
var propertyInfos1 = type1.GetProperties();
var propertyInfos2 = type2.GetProperties();
// To use the access via index, the list have to be ordered!
var propertyInfoOrdered1 = propertyInfos1.OrderBy(p => p.Name).ToArray();
var propertyInfoOrdered2 = propertyInfos2.OrderBy(p => p.Name).ToArray();
if (type1 != type2) errorList.AddRange(new List<object> {type1, type2});
else
{
for (var i = 0; i < propertyInfos1.Length; i++)
{
var t1 = propertyInfoOrdered1[i].PropertyType;
var t2 = propertyInfoOrdered2[i].PropertyType;
if (t1 != t2)
{
errorList.AddRange(new List<object> {type1, type2});
continue;
}
var name1 = propertyInfoOrdered1[i].Name;
var name2 = propertyInfoOrdered2[i].Name;
// Use the next 4 lines to find a bug
//if (name1 == "Enter name of field with the bug")
// Console.WriteLine(name1);
//if (name2 == "Enter name of field1 with the bug" || name2 == "Enter name of field2 with the bug")
// Console.WriteLine(name2);
if (consideredFieldNames != null && !consideredFieldNames.Contains(name1)) continue;
if (notConsideredFieldNames != null && notConsideredFieldNames.Contains(name1)) continue;
var value1 = propertyInfoOrdered1[i].GetValue(obj1, null);
var value2 = propertyInfoOrdered2[i].GetValue(obj2, null);
// check Attributes
var guiName1 = (propertyInfoOrdered1[i].GetCustomAttributes().FirstOrDefault(a => a.GetType() == typeof(GuiNameofModelAttribute)) as GuiNameofModelAttribute)?.GuiName;
var guiName2 = (propertyInfoOrdered2[i].GetCustomAttributes().FirstOrDefault(a => a.GetType() == typeof(GuiNameofModelAttribute)) as GuiNameofModelAttribute)?.GuiName;
// create errorListrange
var temperrorListRange = new List<object>();
if (guiName1 != null && guiName2 != null) temperrorListRange.AddRange(new List<object> { guiName1, guiName2 });
else temperrorListRange.AddRange(new List<object> { propertyInfoOrdered1[i], propertyInfoOrdered2[i] });
// both fields are null = OK
if ((value1 == null && value2 == null) || (value1 is Guid && value2 is Guid)) continue;
// one of the fields is null = errorList
if (value1 == null || value2 == null) errorList.AddRange(temperrorListRange);
// Value types, Enum and String compair
else if (t1.BaseType == typeof (ValueType) || t1.BaseType == typeof (Enum) || t1 == typeof (string))
{
if (!value1.Equals(value2)) errorList.AddRange(temperrorListRange);
}
// List, array, generic lists, collection and bindinglist compair
else if (t1 == typeof (Array) || (t1.IsGenericType && (t1.GetGenericTypeDefinition() == typeof (List<>) ||
t1.GetGenericTypeDefinition() == typeof (IList<>) ||
t1.GetGenericTypeDefinition() == typeof (Collection<>) ||
t1.GetGenericTypeDefinition() == typeof (ICollection<>) ||
t1.GetGenericTypeDefinition() == typeof (ObservableCollection<>) ||
t1.GetGenericTypeDefinition() == typeof (BindingList<>) ||
t1.GetGenericTypeDefinition() == typeof (BindingList<>)
)))
DeepListCompare(value1 as IEnumerable<object>, value2 as IEnumerable<object>, errorList, consideredFieldNames, notConsideredFieldNames, endWithErrors);
// Clas compair
else if (t1.IsClass || t1.IsAnsiClass) DeepCompare(value1, value2, errorList, consideredFieldNames, notConsideredFieldNames, endWithErrors);
else throw new NotImplementedException();
if (!endWithErrors && errorList.Count > 0) break;
}
}
} // End DeepCompare<T>
/// <summary>
/// Compairs two lists and gives back true if they are equal.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tlist1">Generic list 1</param>
/// <param name="tlist2">Generic List 2</param>
/// <returns></returns>
public static bool DeepListCompare<T>(T tlist1, T tlist2)
{
var errorList = new List<object>();
DeepCompare(tlist1, tlist2, errorList, null, null, false);
return errorList.Count <= 0;
}
/// <summary>
/// Compairs two lists and gives backthe error list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tlist1">Generic list 1</param>
/// <param name="tlist2">Generic list 2</param>
/// <param name="errorList">The error list gives back the names of the fields that are not equal.</param>
/// <param name="consideredFieldNames">If the list is not mepty, only the field within equal names are compaired.</param>
/// <param name="notConsideredFieldNames">If you want not compair some fields enter their name in this list.</param>
/// <param name="endWithErrors">If the value is false, the method end at the first error.</param>
public static void DeepListCompare<T>(T tlist1, T tlist2, List<object> errorList, string[] consideredFieldNames, string[] notConsideredFieldNames, bool endWithErrors)
where T : IEnumerable<object>
{
if (errorList == null) throw new Exception("errorListliste ist NULL");
if (!endWithErrors && errorList.Count > 0) throw new Exception("errorListliste ist nicht Leer");
if (Equals(tlist1, null) || Equals(tlist2, null))
{
errorList.AddRange(new List<object> {tlist1, tlist2});
return;
}
var type1 = tlist1.GetType();
var type2 = tlist2.GetType();
var propertyInfos1 = type1.GetProperties();
var propertyInfos2 = type2.GetProperties();
// To use the access via index, the list have to be ordered!
var propertyInfoOrdered1 = propertyInfos1.OrderBy(p => p.Name).ToArray();
var propertyInfoOrdered2 = propertyInfos2.OrderBy(p => p.Name).ToArray();
for (var i = 0; i < propertyInfos1.Length; i++)
{
var t1 = propertyInfoOrdered1[i].PropertyType;
var t2 = propertyInfoOrdered2[i].PropertyType;
if (t1 != t2) errorList.AddRange(new List<object> {t1, t2});
else
{
// Kick out index
if (propertyInfoOrdered1[i].GetIndexParameters().Length != 0)
{
continue;
}
// Get value
var value1 = propertyInfoOrdered1[i].GetValue(tlist1, null) as IEnumerable<object>;
var value2 = propertyInfoOrdered2[i].GetValue(tlist2, null) as IEnumerable<object>;
if (value1 == null || value2 == null) continue;
// Only run through real lists.
if (t1 == typeof (Array) ||
t1.IsGenericType && t1.GetGenericTypeDefinition() == typeof (List<>) ||
t1.IsGenericType && t1.GetGenericTypeDefinition() == typeof (Collection<>))
{
// cast
var objectList1 = value1.ToList();
var objectList2 = value2.ToList();
if (objectList1.Count == 0 && objectList1.Count == 0)
{
//errorList.AddRange(new List<Object> { objectList1, objectList1 });
continue;
}
foreach (var item1 in objectList1)
{
foreach (var item2 in objectList2)
{
var temperrorListCount = errorList.Count;
DeepCompare(item1, item2, errorList, consideredFieldNames, notConsideredFieldNames, endWithErrors);
if (temperrorListCount != errorList.Count) continue;
objectList2.Remove(item2);
break;
}
if (!endWithErrors && errorList.Count > 0) break;
}
}
else DeepCompare(value1, value2, errorList, consideredFieldNames, notConsideredFieldNames, endWithErrors);
}
if (!endWithErrors && errorList.Count > 0) break;
}
} // End DeepListCompare<T>
} // end class ObjectHelper
[AttributeUsage(AttributeTargets.All)]
public class GuiNameofModelAttribute : Attribute
{
public readonly string GuiName;
public GuiNameofModelAttribute(string guiName)
{
GuiName = guiName;
}
}
You could simply write a utility method in another class to do the comparison. However, that is assuming the properties of the class in question are publicly accessible. Are they?
Thanks for the MemoryStream approach, Marc. I thought sure there was a mistake in when I saw "this" in the arguments, but surprisingly the compiler actually lets you do it that way, huh? I made a slight change and chose to override to Equals() instead. Kudos also for using length and array comparison rather than SequenceEquals(). Takes an extra minute to write, but according to http://www.dotnetperls.com/sequenceequal, the performance is much better.
public override bool Equals(object obj)
{
// If comparison object is null or is a different type, no further comparisons are necessary...
if (obj == null || GetType() != obj.GetType())
{
return false;
}
// Compare objects using byte arrays...
using (MemoryStream memStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
// Get byte array of "this" object...
binaryFormatter.Serialize(memStream, this);
byte[] b1 = memStream.ToArray();
// Get byte array of object to be compared...
memStream.SetLength(0);
binaryFormatter.Serialize(memStream, obj);
byte[] b2 = memStream.ToArray();
// Compare array sizes. If equal, no further comparisons are necessary...
if (b1.Length != b2.Length)
return false;
// If sizes are equal, compare each byte while inequality is not found...
for (int i = 0; i < b1.Length; i++)
{
if (b1[i] != b2[i])
return false;
}
}
return true;
}
Well, you could write some logic to compare all of the properties of the two objects to each other. This gets complicated when it is an object graph with complex subtypes, so you will need to determine how close is good enough.
You would need a compare method of something else; in C++ you could just write a global function, but I don't think c# allows that, just as Java doesn't.
What I'd do is write a class that implements iComparable, and has a ctor that takes an object of your desired class, and includes your Equals function. Set it up so all it keeps is a reference to the original object, to save mallocations.
Then you can write
Foo(A).Equals(new Foo(B))
You could instead inherit from the provided class, but that would mean needing to create and track these things.
Related
This question already has answers here:
Check two object, of unknown type, for equality, comparing all their fields
(2 answers)
Closed 3 years ago.
I'm trying to make a generic compare function to see if two instances of a class which may hold member variables that are a List<>.
bool IsEqual<T>(T a, T b)
{
var props = typeof(T).GetProperties();
foreach(var property in props)
{
var aVal = property.GetValue(a);
var bVal = property.GetValue(b);
if (aVal as System.Collections.IEnumerable != null)
{
// Convert into lists, do something with SequenceEqual and possibly recursive call to IsEqual
}
if (!Equals(aVal,bVal)) return false;
}
return true;
}
I'm not sure how to convert a given member variable to a proper list, so I can do a SequenceEqual on it to compare the lists.
The classes that I will use in IsEqual() are not native to my program but comes from a DLL.
Does the following code work for you?
public static bool DeepEquals(object a, object b)
{
if (a == null)
{
if (b == null) return true;
else return false;
}
else if (b == null) return false;
if (object.ReferenceEquals(a, b)) return true;
var comparable = a as IComparable;
if (comparable != null)
{
return comparable.CompareTo(b) == 0;
}
var aType = a.GetType();
var bType = b.GetType();
if (aType != bType) return false;
var aEnumerable = a as IEnumerable;
if (aEnumerable != null)
{
var bEnumerable = (IEnumerable)b;
var aEnumerator = aEnumerable.GetEnumerator();
var bEnumerator = bEnumerable.GetEnumerator();
while (true)
{
var amoved = aEnumerator.MoveNext();
var bmoved = bEnumerator.MoveNext();
if (amoved != bmoved) return false;
if (amoved == false && bmoved == false) return true;
if (DeepEquals(aEnumerator.Current, bEnumerator.Current) == false) return false;
}
}
var props = aType.GetProperties();
foreach (var prop in props)
{
if (DeepEquals(prop.GetValue(a), prop.GetValue(b)) == false) return false;
}
return true;
}
Note that this method is not generic. Implementing a generic method is more difficult because you don't know at runtime the type of properties nor the type of the items of collections.
Given an object and an instance method, I want to get a method that has the exact same signature but with a different, known name. That includes parameter list, generic parameters, possibly attributes if they're a part of the signature. How can I do that?
I know GetMethod() exists but I couldn't figure out which overload covers all the different possible signature variations.
Something like this may work in some cases:
public static bool HasSameSignature(MethodInfo m1, MethodInfo m2)
{
if (m1.GetGenericArguments().Length != m2.GetGenericArguments().Length)
return false;
var args1 = m1.GetParameters();
var args2 = m2.GetParameters();
if (args1.Length != args2.Length)
return false;
for (var idx = 0; idx < args1.Length; idx++)
{
if (!AreEquivalentTypes(args1[idx].ParameterType, args2[idx].ParameterType))
return false;
}
return true;
}
static bool AreEquivalentTypes(Type t1, Type t2)
{
if (t1 == null || t2 == null)
return false;
if (t1 == t2)
return true;
if (t1.DeclaringMethod != null && t2.DeclaringMethod != null && t1.GenericParameterPosition == t2.GenericParameterPosition)
return true;
if (AreEquivalentTypes(t1.GetElementType(), t2.GetElementType()))
return true;
if (t1.IsGenericType && t2.IsGenericType && t1.GetGenericTypeDefinition() == t2.GetGenericTypeDefinition())
{
var ta1 = t1.GenericTypeArguments;
var ta2 = t2.GenericTypeArguments;
for (var idx = 0; idx < ta1.Length; idx++)
{
if (!AreEquivalentTypes(ta1[idx], ta2[idx]))
return false;
}
return true;
}
return false;
}
So given your one method, you can do .GetMethods() on the type in question, and find the one whose name is correct, and for which HasSameSignature(...) on it and your one given method is true.
Type givenType = ...;
MethodInfo givenMethod = ...;
string givenDifferentName = ...;
var answer = givenType.GetMethods()
.SingleOrDefault(m => m.Name == givenDifferentName && HasSameSignature(m, givenMethod));
Examples,
"Namespace.ClassName+NestedClassName" becomes "Namespace.ClassName.NestedClassName".
"System.Collections.Generic.List`1[System.String]" becomes "System.Collections.Generic.List".
I need this for a code generator and I don't want to do a simple RegEx replace because of edge cases such as type names with '+' symbols in them. (Yes, though stupid that is possible.)
/// <summary>
/// Gets the fully quantified name in C# format.
/// </summary>
public string CSharpFullName
{
get
{
if (m_CSharpFullName == null)
{
var result = new StringBuilder(m_TypeInfo.ToString().Length);
BuildCSharpFullName(m_TypeInfo.AsType(), null, result);
m_CSharpFullName = result.ToString();
}
return m_CSharpFullName;
}
}
static void BuildCSharpFullName(Type typeInfo, List<Type> typeArgs, StringBuilder result)
{
var localTypeParamCount = typeInfo.GetTypeInfo().GenericTypeParameters.Length;
var localTypeArgCount = typeInfo.GetTypeInfo().GenericTypeArguments.Length;
var typeParamCount = Math.Max(localTypeParamCount, localTypeArgCount);
if (typeArgs == null)
typeArgs = new List<Type>(typeInfo.GetTypeInfo().GenericTypeArguments);
if (typeInfo.IsNested)
{
BuildCSharpFullName(typeInfo.DeclaringType, typeArgs, result);
}
else
{
result.Append(typeInfo.Namespace);
}
result.Append(".");
foreach (var c in typeInfo.Name)
{
if (c == '`') //we found a generic
break;
result.Append(c);
}
if (localTypeParamCount > 0)
{
result.Append("<");
for (int i = 0; i < localTypeParamCount; i++)
{
if (i > 0)
result.Append(",");
BuildCSharpFullName(typeArgs[i], null, result); //note that we are "eating" the typeArgs that we passed to us from the nested type.
}
typeArgs.RemoveRange(0, localTypeParamCount); //remove the used args
result.Append(">");
}
else if (localTypeArgCount > 0 && typeArgs.Count > 0)
{
result.Append("<");
for (int i = 0; i < Math.Min(localTypeArgCount, typeArgs.Count); i++)
{
if (i > 0)
result.Append(",");
BuildCSharpFullName(typeArgs[i], null, result);
}
result.Append(">");
}
}
You could use Type.GetType to parse the string into a Type object. From there you could use GetGenericArguments and other members to get the Type info of the individual components. You could use those to reconstruct your target string.
I have a list of property paths and values as strings in a text file. Is there a mapping tool or serialize that could take a property path string( "Package.Customer.Name" ) and create the object and set the value?
/// <summary>
/// With a data row, create an object and populate it with data
/// </summary>
private object CreateObject(List<DataCell> pRow, string objectName)
{
Type type = Type.GetType(objectName);
if (type == null)
throw new Exception(String.Format("Could not create type {0}", objectName));
object obj = Activator.CreateInstance(type);
foreach (DataCell cell in pRow)
{
propagateToProperty(obj, *propertypath*, cell.CellValue);
}
return obj;
}
public static void propagateToProperty(object pObject, string pPropertyPath, object pValue)
{
Type t = pObject.GetType();
object currentO = pObject;
string[] path = pPropertyPath.Split('.');
for (byte i = 0; i < path.Length; i++)
{
//go through object hierarchy
PropertyInfo pi = t.GetProperty(path[i]);
if (pi == null)
throw new Exception(String.Format("No property {0} on type {1}", path[i], pObject.GetType()));
//an object in the property path
if (i != path.Length - 1)
{
t = pi.PropertyType;
object childObject = pi.GetValue(currentO, null);
if (childObject == null)
{
childObject = Activator.CreateInstance(t);
pi.SetValue(currentO, childObject, null);
}
currentO = childObject;
//the target property
else
{
if (pi.PropertyType.IsEnum && pValue.GetType() != pi.PropertyType)
pValue = Enum.Parse(pi.PropertyType, pValue.ToString());
if (pi.PropertyType == typeof(Guid) && pValue.GetType() != pi.PropertyType)
pValue = new Guid(pValue.ToString());
if (pi.PropertyType == typeof(char))
if (pValue.ToString().Length == 0)
pValue = ' ';
else
pValue = pValue.ToString()[0];
pi.SetValue(currentO, pValue, null);
}
}
}
Thats what I use. I just had it open in VS, maybe it can help you. The propertyPath is a placeholder for your property path, i cut that out from my code.
If you re looking for a more robust solution, Automapper would probably work nicely, as suggested by someone else already.
What would be the best way to determine if an object equals number zero (0) or string.empty in C#?
EDIT: The object can equal any built-in System.Value type or reference type.
Source Code:
public void MyMethod(object input1, object input2)
{
bool result = false;
object compare = new object();
if(input != null && input2 != null)
{
if(input1 is IComparable && input2 is IComparable)
{
//do check for zero or string.empty
//if input1 equals to zero or string.empty
result = object.Equals(input2);
//if input1 not equals to zero or string.empty
result = object.Equals(input1) && object.Equals(input2); //yes not valid, but this is what I want to accomplish
}
}
}
Using Jonathan Holland code sample with a minor modification, here is the solution that worked:
static bool IsZeroOrEmpty(object o1)
{
bool Passed = false;
object ZeroValue = 0;
if(o1 != null)
{
if(o1.GetType().IsValueType)
{
Passed = (o1 as System.ValueType).Equals(Convert.ChangeType(ZeroValue, o1.GetType()))
}
else
{
if (o1.GetType() == typeof(String))
{
Passed = o1.Equals(String.Empty);
}
}
}
return Passed;
}
What's wrong with this?
public static bool IsZeroOrEmptyString(object obj)
{
if (obj == null)
return false;
else if (obj.Equals(0) || obj.Equals(""))
return true;
else
return false;
}
Michael, you need to provide a little bit more information here.
strings can be compared to null or string.Empty by using the method
string x = "Some String"
if( string.IsNullOrEmpty(string input) ) { ... }
int, decimals, doubles (and other numeric value-types) can be compared to 0 (zero) with a simple == test
int x = 0;
if(x == 0) { ... }
You can also have nullable value-types also by using the ? operator when you instantiate them. This allows you to set a value type as null.
int? x = null;
if( !x.HasValue ) { }
For any other object, a simple == null test will tell you if its null or not
object o = new object();
if( o != null ) { ... }
Hope that sheds some light on things.
Not quite sure the reasoning behind this, because .Equals is reference equality on reference types, and value equality on value types.
This seems to work, but I doubt its what you want:
static bool IsZeroOrEmpty(object o1)
{
if (o1 == null)
return false;
if (o1.GetType().IsValueType)
{
return (o1 as System.ValueType).Equals(0);
}
else
{
if (o1.GetType() == typeof(String))
{
return o1.Equals(String.Empty);
}
return o1.Equals(0);
}
}
Do you mean null or string.empty, if you're talking about strings?
if (String.IsNullOrEmpty(obj as string)) { ... do something }
Oisin
In the first case by testing if it is null. In the second case by testing if it is string.empty (you answered your own question).
I should add that an object can never be equal to 0. An object variable can have a null reference though (in reality that means the variable has the value of 0; there is no object in this case though)
obj => obj is int && (int)obj == 0 || obj is string && (string)obj == string.Empty