I'm trying to implement the common/general DebuggerDisplay attribute to avoid formatting it for every single usage which would simply show every property of the class. That's what I've achieved so far, my extension method looks like this
public static class DebuggerExtension
{
public static string ToDebuggerString(this object #this)
{
var builder = new StringBuilder();
foreach (var property in #this.GetType().GetProperties())
{
if (property != null)
{
var value = property.GetValue(#this, null)?.ToString();
builder.Append($"{property.Name}: {value}, ");
}
}
return builder.ToString();
}
}
and I'm calling it like this and it basically works:
[DebuggerDisplay($"{{this.ToDebuggerString()}}")]
My questions are:
Is there any existing extension to generate the DebuggerDisplay string? The built in VS implementation just adds the method which returns ToString() which doesn't really help
Can I call the method on some compiler-checked way, not as a string? "this." is obviously not available in the attribute arguments. Maybe any expression?
Any other possible solutions?
Have you tried using 'ToDebuggerString()' again instead of 'ToString()' in extension method?
It may interest you. Visual Studio's Natvis Debugging Framework Tutorial
The example below may be helpful to you. (It may be missing!)
public static class DebuggerExtension
{
public static string ToDebuggerDisplay(this object o)
{
if (object.ReferenceEquals(o, null))
return "null";
else if (o is string)
return string.Format("\"{0}\"", (string)o);
else if ((o is DateTime) || (o is TimeSpan))
return string.Format("{{{0}}}", o);
else if (o is System.Collections.ICollection)
return string.Format("{{Count={0}}}", ((System.Collections.ICollection)o).Count);
else if (o is System.Collections.IEnumerable)
{
int nCount = 0;
System.Collections.IEnumerator e = ((System.Collections.IEnumerable)o).GetEnumerator();
while (e.MoveNext())
{
nCount++;
}
return string.Format("{{Count={0}}}", nCount);
}
Type objType = o.GetType();
if (objType.IsPrimitive || objType.IsEnum)
return o.ToString();
else if (objType.IsArray)
return string.Format("{{Count={0}}}", ((Array)o).Length);
PropertyInfo[] propertyInfos = objType.GetProperties(BindingFlags.Instance
| BindingFlags.Public
| BindingFlags.GetProperty
| BindingFlags.DeclaredOnly);
string retVal = "{";
int ndx = 0;
foreach (PropertyInfo pi in propertyInfos)
{
if (!pi.CanRead)
continue;
DebuggerBrowsableAttribute attr = pi.GetCustomAttribute<DebuggerBrowsableAttribute>();
if (!object.ReferenceEquals(attr, null) && (attr.State == DebuggerBrowsableState.Never))
continue;
if (ndx++ > 0)
retVal += " ";
retVal += string.Format("{0}={1}", pi.Name, pi.GetValue(o).ToDebuggerDisplay());
if (ndx > 2)
{
retVal += " ...";
break;
}
}
retVal += "}";
return (ndx > 0) ? retVal : o.ToString();
}
}
Uses
[DebuggerDisplay(#"{this.ToDebuggerDisplay(),nq}")]
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));
I have written the code below to evaluate a boolean expression. The expression is coded in the form of objects.
It's one of those moments when I look at the code and think: I'm sure there's a better way to code that, using less boolean variables but can't see the right way to go. Any help? Unit tests have been written and are passing for a variety of inputs.
if (tree == null || !tree.IsActive || tree.FilterNodes == null)
{
return false;
}
var result = false;
foreach (var filter in tree.FilterNodes.Where(a => a.IsActive && a.ConditionNodes != null))
{
var tempBool = false;
foreach (var condition in filter.ConditionNodes.Where(a => a.IsActive))
{
if (!string.IsNullOrWhiteSpace(condition.FieldName) && values.ContainsKey(condition.FieldName))
{
var value = values[condition.FieldName];
if (filter.LogicalOperator == LogicalOperator.Or && ApplyCondition(condition.ConditionOperator, value, condition.FieldValue))
{
tempBool = true;
break;
}
else if (filter.LogicalOperator == LogicalOperator.And)
{
tempBool = ApplyCondition(condition.ConditionOperator, value, condition.FieldValue);
if (!tempBool)
{
break;
}
}
else
{
tempBool = false;
}
}
else if (!string.IsNullOrWhiteSpace(condition.FieldName) && filter.LogicalOperator == LogicalOperator.And)
{
tempBool = false;
}
}
result = tempBool;
if (!result)
{
break;
}
}
return result;
You could set tempBool = false first thing in the loop and leave out the else and last else if:
foreach (var condition in filter.ConditionNodes.Where(a => a.IsActive))
{
tempBool = false;
if (!string.IsNullOrWhiteSpace(condition.FieldName) && values.ContainsKey(condition.FieldName))
{
var value = values[condition.FieldName];
if (filter.LogicalOperator == LogicalOperator.Or && ApplyCondition(condition.ConditionOperator, value, condition.FieldValue))
{
tempBool = true;
break;
}
else if (filter.LogicalOperator == LogicalOperator.And)
{
tempBool = ApplyCondition(condition.ConditionOperator, value, condition.FieldValue);
if (!tempBool)
{
break;
}
}
}
}
EDIT
It gets even simpler:
foreach (var condition in filter.ConditionNodes.Where(a => a.IsActive))
{
tempBool = false;
if (!string.IsNullOrWhiteSpace(condition.FieldName) && values.ContainsKey(condition.FieldName))
{
var value = values[condition.FieldName];
tempBool == ApplyCondition(condition.ConditionOperator, value, condition.FieldValue);
if ((filter.LogicalOperator == LogicalOperator.And && !tempBool) || (filter.LogicalOperator == LogicalOperator.Or && tempBool))
{
break;
}
}
}
In the if you need ApplyCondition to be true and then set tempBool to true (also the result of ApplyCondition). In the else if you set tempBoolto the result of ApplyCondition. That means you can set tempBoolto the result of ApplyConditionin the first place. Now you just need to decide if you need to break.
Taking a more o-o approach, I think your operators need to be defined by classes that inherit from a base class. The base class would have an abstract Evaluate method that your operators implement. You can then use o-o polymorphism to evaluate your operators without worrying about the internal details. Effectively you have the beginnings of a simple interpreter.
A more formal way to code a boolean interpreter is considering a boolean expression as generated by a formal grammar and writing a parser and an interpreter for it. The interpreter could be implemented as an abstract syntax tree.
I made an open source library to achieve this, if you want you can take a look on GitHub.
This question already has answers here:
Closed 10 years ago.
Possible Duplicates:
What is “Best Practice” For Comparing Two Instances of a Reference Type?
How do I check for nulls in an '==' operator overload without infinite recursion?
I have a class called "Criterion" and i want to implement == operator,but I'm struggling with the following problem :
When i implement the == operator, I'm checking if one or both of my instances are null, but when i do it, it causes a recursive call for == and then i get "StackOverflow" (he he) exception.
Technically i can implement the Equals operator and not override the ==, but the code will be much more readable if i implement the == operator.
Here is my code :
public static bool operator == (Criterion c1, Criterion c2)
{
if (null == c1)
{
if (null == c2)
return true;
return false;
}
if (null == c2)
return false;
if ((c1.mId == c2.mId) && (c1.mName == c2.mName))
return true;
return false;
}
Try ReferenceEquals:
public static bool operator ==(Criterion c1, Criterion c2) {
var nullC1 = ReferenceEquals(null, c1);
var nullC2 = ReferenceEquals(null, c2);
if (nullC1 && nullC2)
return true;
if (!nullC1 && !nullC2)
if (c1.mId == c2.mId && c1.mName == c2.mName)
return true;
return false;
}
I'd do it like this:
bool isC1Null = Object.Equals(c1, null)
bool isC2Null = Object.Equals(c2, null)
if (isC1Null ^ isC2Null)
{
return false
}
if (isC1Null && isC2Null)
{
return true
}
//your code
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.