c# compare the data in two object models - c#

I have a dialog, when spawned it gets populated with the data in an object model. At this point the data is copied and stored in a "backup" object model. When the user has finished making their changes, and click "ok" to dismiss the dialog, I need a quick way of comparing the backup object model with the live one - if anything is changed I can create the user a new undo state.
I don't want to have to go and write comparison function for every single class in the object model if possible.
If I serialised both object models and they were identical but stored in different memory locations would they be equal? Does some simple way exist to compare two serialised object models?

I didn't bother with a hash string but just a straight Binary serialisation works wonders. When the dialog opens serialise the object model.
BinaryFormatter formatter = new BinaryFormatter();
m_backupStream = new MemoryStream();
formatter.Serialize(m_backupStream,m_objectModel);
Then if the user adds to the object model using available controls (or not). When the dialog closes you can compare to the original serialisation with a new one - this for me is how i decide whether or not an Undo state is required.
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream liveStream = new MemoryStream();
formatter.Serialize(liveStream,m_objectModel);
byte[] streamOneBytes = liveStream.ToArray();
byte[] streamTwoBytes = m_backupStream.ToArray();
if(!CompareArrays(streamOneBytes, streamTwoBytes))
AddUndoState();
And the compare arrays function incase anybody needs it - prob not the best way of comparing two arrays im sure.
private bool CompareArrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
for (int i = 0; i < a.Length;i++)
{
if (a[i] != b[i])
return false;
}
return true;
}

I'd say the best way is to implement the equality operators on all classes in your model (which is usually a good idea anyway if you're going to do comparisons).
class Book
{
public string Title { get; set; }
public string Author { get; set; }
public ICollection<Chapter> Chapters { get; set; }
public bool Equals(Book other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.Title, Title) && Equals(other.Author, Author) && Equals(other.Chapters, Chapters);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (Book)) return false;
return Equals((Book) obj);
}
public override int GetHashCode()
{
unchecked
{
int result = (Title != null ? Title.GetHashCode() : 0);
result = (result*397) ^ (Author != null ? Author.GetHashCode() : 0);
result = (result*397) ^ (Chapters != null ? Chapters.GetHashCode() : 0);
return result;
}
}
}
This snippet is auto-generated by ReSharper, but you can use this as a basis. Basically you will have to extend the non overriden Equals method with your custom comparison logic.
For instance, you might want to use SequenceEquals from the Linq extensions to check if the chapters collection is equal in sequence.
Comparing two books will now be as simple as saying:
Book book1 = new Book();
Book book2 = new Book();
book1.Title = "A book!";
book2.Title = "A book!";
bool equality = book1.Equals(book2); // returns true
book2.Title = "A different Title";
equality = book1.Equals(book2); // returns false
Keep in mind that there's another way of implementing equality: the System.IEquatable, which is used by various classes in the System.Collections namespace for determining equality.
I'd say check that out as well and you're well on your way!

I understand your question to be how one can compare two objects for value equality (as opposed to reference equality) without prior knowledge of the types, such as if they implement IEquatable or override Equals.
To do this I recommend two options:
A. Use an all-purpose serialization class to serialize both objects and compare their value. For example I have a class called XmlSerializer that takes any object and serializes its public properties as an XML document. Two objects that have the same values and possibly the same reference will have the same values in this sense.
B. Using reflection, compare the values of all of the properties of both objects, like:
bool Equal(object a, object b)
{
// They're both null.
if (a == null && b == null) return true;
// One is null, so they can't be the same.
if (a == null || b == null) return false;
// How can they be the same if they're different types?
if (a.GetType() != b.GetType()) return false;
var Props = a.GetType().GetProperties();
foreach(var Prop in Props)
{
// See notes *
var aPropValue = Prop.GetValue(a) ?? string.Empty;
var bPropValue = Prop.GetValue(b) ?? string.Empty;
if(aPropValue.ToString() != bPropValue.ToString())
return false;
}
return true;
}
Here we're assuming that we can easily compare the properties, like if they all implement IConvertible, or correctly override ToString. If that's not the case I would check if they implement IConvertible and if not, recursively call Equal() on the properties.
This only works if you're content with comparing public properties. Of course you COULD check private and protected fields and properties too, but if you know so little about the objects you're probably asking for trouble but doing so.

Related

GetHashCode functionality

Why two object's hash code is not same even though they have similar values. What is the other best approach to find value equality among objects with out reading ones each property and have check with other ones property?
Person person = new Person();
person.Name = "X";
person.Age = 25;
person.Zip = 600056;
person.Sex = 'M';
Person person1 = new Person();
person1.Name = "X";
person1.Age = 25;
person1.Zip = 600056;
person1.Sex = 'M';
int hashCode1 = person1.Name.GetHashCode();
int hashCode = person.Name.GetHashCode();
// hashCode1 and hashCode values are same.
if (person.GetHashCode() == person1.GetHashCode())
{
// Condition is not satisfied
}
in your code hashCode1 == hashCode is true because hashing same string will always give you the same result. however insances are different so you have to override GetHashCode() in a way that fits your business logic for example
public override int GetHashCode()
{
return Name.GetHashCode() ^ Zip.GetHashCode() ^ Sex ^ Age;
}
I sugest you take a look at this answer GetHashCode Guidelines in C#.
and http://musingmarc.blogspot.com/2007/08/vtos-rtos-and-gethashcode-oh-my.html
In your example, both objects are alike but not the same. They are different instances, so they have different hash code. Also person1 == person2 and person1.Equals(person2) are false.
You can override this behavior. If you consider the two objects are the same if those properties are equal, the you can:
public override bool Equals(object other) {
if(other == this) return true;
var person = other as Person;
if(person == null) return false;
return person.Name == Name && person.Age == Age && person.Zip == Zip && person.Sex == Sex;
 }
public override int GetHashCode() {
//some logic to create the Hash Code based on the properties. i.e.
return (Name + Age + Zip + Sex).GetHashCode(); // this is just a bad example!
}
Why two object's hash code is not same even though they have similar values
Because that's the expected way of identifying them uniquely. AFAIK, most frameworks/libraries uses the pointer value for this by default.
What is the other best approach to find value equality among objects with out reading ones each property and have check with other ones property?
Depends on what makes them "equal", according to your needs. Basically it's still comparing properties, but maybe just more limited.

Finding a Needle (object) in a Haystack (parent object) Entity Framework 4

I have an Entity Framework 4 project that has built up some brute-force searching code that I'd like to reduce to more generic, and more manageable chunks.
One of my Partial Classes, the Run object, contains Navigation Properties (Entity Collections) to other objects (Run.Nodes, Run.Arcs), as well as Scalars (GUID, Version #), and singlular Navigation Properties (Entity Objects - Run.TimeRange).
Run.Nodes is a Base Class collection of NodeBase, with derived classes of NodeTypeA, NodeTypeB, and NodeTypeC.
Using Reflection:
public EntityObject FindDiscriminant<T>(T needle) where T : EntityObject
{
Boolean test = false;
Type sourceType = this.GetType();
String needleString = needle.GetType().BaseType.Name.ToString();
String needleStringLookup = typeDict.Where(o => o.Key == needleString).FirstOrDefault().Value;
//If we don't match anything that means that the object itself is a base class, so we need to try again
if (needleStringLookup == null)
{
needleString = needle.GetType().Name.ToString();
needleStringLookup = typeDict.Where(o => o.Key == needleString).FirstOrDefault().Value;
}
var needleProperty = Type.GetType(sourceType.FullName).GetProperty(needleStringLookup);
var runValue = needleProperty.GetValue(this, null);
if (runValue.GetType().ToString().Contains("EntityCollection"))
{
foreach (var obj in (runValue as EntityCollection<T>).ToList())
{
test = (obj as T).Discriminant(needle);
if (test == true)
return obj;
}
}
else
{
test = (runValue as EntityObject).Discriminant(needle);
if (test == true)
return (T)runValue;
}
return null;
}
This method works great for EntityCollections (except NodeBase). If I try and look for a node of NodeTypeC in Run.Nodes, runValue will be an EntityCollection of 173 NodeBase objects. But when I try and iterate over it (.ToList()), I get this error:
System.ArgumentNullException was unhandled
Value cannot be null.
Parameter name: source
My workaround is to check to see of the EntityCollection is of type NodeBase, and have an if statement to handle it, and substitute EntityCollection).ToList() for EntityCollection).ToList()
Any suggestions?
An update to my question, for anyone searching this. The code has changed dramatically, and I'm now using Delegates as SearchActions, and have a generic FindSomething routine that uses those delegates instead of having several search routines each using their own type of input.
The things to note are:
The method of detection for determining if my object I pulled with
reflection is an EntityObject or an EntityCollection
I use a private method to iterate over the EntityCollection that I
pass from my generic FindSomething routine. This takes care of the
base-class comparisons
By having the private method to call, I avoid having to use casting on the EntityCollection - this goes away: (runValue as EntityCollection) as well as (obj as T)
I have created a dynamic Object Dictionary when I instantiate our
application - I go through our collection of objects and map objects
and the properties we care about so I don't have to brute force
through an entire object every search
I use dynamic instead of var - I love dynamic! And I no longer cast
before doing a search.
The function is recursive - the SearchAction delegate gets called
again during the iteration code in the IterateThroughEntityCollection
method.
Good? Bad? Comments? Feedback? It works for me, and it's fast.
Here's the revised code:
private EntityObject FindSomething<T>(Run haystack, T needle, SearchAction<T> sa)
{
//First, assume we haven't found anything
Boolean test = false;
//Next, go through all the objects in a run and see if we find anything
//No need to go through Arcs, if the needle is a type of NodeBase, etc.
Type oldRunElementProperty = TypeReference.RunElementDictionary.Where(o => o.Key == type).Single().Key;
PropertyInfo runValuePropertyToChange = TypeReference.RunElementDictionary.Where(o => o.Key == type).Single().Value;
dynamic runValue = runValuePropertyToChange.GetValue(haystack, null);
//Check to see if we're dealing with an EntityCollection or an EntityObject. If it is an EntityObject, we can set that value
//directly. If it is a collection, we need to use the generic method
if (runValuePropertyToChange.PropertyType.IsGenericType && runValuePropertyToChange.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
{
EntityObject result = IterateThroughEntityCollection(runValue, needle, sa);
if (result != null) return result;
}
else
{
test = sa(runValue, needle); if (test == true) return runValue;
}
return null;
}
Private EntityCollection iterator.
private EntityObject IterateThroughEntityCollection<T,U>(EntityCollection<T> haystack, U needle, SearchAction<U> sa) where T: EntityObject
{
Boolean test = false;
foreach(dynamic obj in haystack)
{
test = sa(obj, needle);
if (test == true) return obj;
}
return null;
}

Intersect between two lists not working

I have two lists see below.....result is coming back as empty
List<Pay>olist = new List<Pay>();
List<Pay> nlist = new List<Pay>();
Pay oldpay = new Pay()
{
EventId = 1,
Number = 123,
Amount = 1
};
olist.Add(oldpay);
Pay newpay = new Pay ()
{
EventId = 1,
Number = 123,
Amount = 100
};
nlist.Add(newpay);
var Result = nlist.Intersect(olist);
any clue why?
You need to override the Equals and GetHashCode methods in your Pay class, otherwise Intersect doesn't know when 2 instances are considered equal. How could it guess that it is the EventId that determines equality? oldPay and newPay are different instances, so by default they're not considered equal.
You can override the methods in Pay like this:
public override int GetHashCode()
{
return this.EventId;
}
public override bool Equals(object other)
{
if (other is Pay)
return ((Pay)other).EventId == this.EventId;
return false;
}
Another option is to implement an IEqualityComparer<Pay> and pass it as a parameter to Intersect:
public class PayComparer : IEqualityComparer<Pay>
{
public bool Equals(Pay x, Pay y)
{
if (x == y) // same instance or both null
return true;
if (x == null || y == null) // either one is null but not both
return false;
return x.EventId == y.EventId;
}
public int GetHashCode(Pay pay)
{
return pay != null ? pay.EventId : 0;
}
}
...
var Result = nlist.Intersect(olist, new PayComparer());
Intersect is probably only adding objects when the same instance of Pay is in both List. As oldPay and newPay are instantiated apart they're considered not equal.
Intersect uses the Equals method to compare objects. If you don't override it it keeps the same behavior of the Object class: returning true only if both are the same instance of the object.
You should override the Equals method in Pay.
//in the Pay class
public override bool Equals(Object o) {
Pay pay = o as Pay;
if (pay == null) return false;
// you haven't said if Number should be included in the comparation
return EventId == pay.EventId; // && Number == pay.Number; (if applies)
}
Objects are reference types. When you create two objects, you have two unique references. The only way they would ever compare equal is if you did:
object a = new object();
object b = a;
In this case, (a == b) is true. Read up on reference vs value types, and objects
And to fix your issue, override Equals and GetHashCode, as Thomas Levesque pointed out.
As others have noted, you need to provide the appropriate overrides to get Intersect to work correctly. But there is another way if you don't want to bother with overrides and your use case is simple. This assumes you want to match items on EventId, but you can modify this to compare any property. Note that this approach is likely more expensive than calling Intersect, but for small data sets it may not matter.
List<Pay> intersectedPays = new List<Pay>();
foreach (Pay o in olist)
{
var intersectedPay = nlist.Where(n => n.EventId == o.EventId).SingleOrDefault();
if (intersectedPay != null)
intersectedPays.Add(intersectedPay);
}
List<Pay> result = intersectedPays;

Cross referencing structure from single data input

Another one of my can't see the wood for the trees questions.
Background
I've got a requirement to query a predefined structure for cooking equivalents across (at the moment) a small range of cultures. The structure won't change but the possibility of other cultures and/or equivalents being introduced is a very strong possibility.
Problem
I would like to put in place a 2nd 'structure' that would allow me to retain the core of what I've been given, but at the same time, allow me to intelligently input the equivalent measurements only once. In the example below, I've only created an equivalent for a UK measurement to return it's partner Metric and US counterparts. My aim would be that from this single input, the structure would be able to spit back a UK equivalent if given a US counterpart etc, etc.
Question
Is it asking too much of such an implicit structure to be able to operate in this fashion. Is it bad practice to ask for such a thing?? What would be your approach to solving such a dilema. The end game would be that any equivalent should be produced as a single liner along the lines of:
// this should produce a value of **CupLitre**
var eqv = conversion["CupUS"][LocaleM.Metric];
Anyway, without further ado:
The Example
Whip up a console app and paste the code below into it (.net v3.5).
using System;
using System.Collections.Generic;
using UnitOfMeasurements;
namespace UnitOfMeasurements
{
[Flags]
public enum LocaleM
{
None = 0, //(0000)
Metric = 1, //(0001)
UK = 2, //(0010)
US = 4, //(0100)
}
}
class Program
{
public static void Main(string[] args)
{
// single structure representing UK equivalents
// this should be extensible to any equivalent
var conversionUK
= new Dictionary<string, Dictionary<LocaleM, string>>
{
{"PintUK",
new Dictionary<LocaleM, string>
{
{LocaleM.US, "PintUS"},
{LocaleM.Metric, "Litre"}
}
},
{"FlOzUK",
new Dictionary<LocaleM, string>
{
{LocaleM.US, "FlOzUS"},
{LocaleM.Metric, "MilliLitre"}
}
},
{"CupUK",
new Dictionary<LocaleM, string>
{
{LocaleM.US, "CupUS"},
{LocaleM.Metric, "CupLitre"}
}
}
};
// basic printout of results
Console.WriteLine(string.Format("{0}\t{1}\t{2}", "Key","US","Metric"));
foreach (var item in conversionUK)
{
Console.WriteLine(string.Format("{0}\t{1}\t{2}",
item.Key,
item.Value[LocaleM.US],
item.Value[LocaleM.Metric]));
}
Console.WriteLine(string
.Format("Here's an example of our direct 'query' - {0}",
conversionUK["CupUK"][LocaleM.Metric]));
Console.Read();
}
}
Good luck - looking fwd to some simple but elegant answers.
[edit] - i must add that the requirement for the inputs ("PintUK", "CupMetric") etc, to be strings is a must as these values will be driving (IVolumeUnit)Activator.CreateInstance(eqv) methods further downstream to create concrete conversion classes of the given string names.
Following approach assumes that all your mappings are bidirectional and if a->b and b->c then a->c
I would introduce a concept of something like default name (cross-culture) for each your item to convert. For example in your code these default names could be 'Pint', 'FlOz' and 'Cup':
UPDATE: replaced custom MapKey class with .Net tuple to make code simplier.
class MapClass
{
private readonly Dictionary<string, string> _localToDefaultNameMap = new Dictionary<string, string>();
private readonly Dictionary<Tuple<string, LocaleM>, string> _defaultNameToLocalMap = new Dictionary<Tuple<string, LocaleM>, string>();
public void AddMapping(string defaultName, LocaleM locale, string localName)
{
_localToDefaultNameMap.Add(localName, defaultName);
_defaultNameToLocalMap.Add(Tuple.Create(defaultName, locale), localName);
}
// maps from source name to target
public string Map(string sourceLocalName, LocaleM targetLocale)
{
string defaultName = _localToDefaultNameMap[sourceLocalName];
var mapKey = Tuple.Create(defaultName, targetLocale);
var localName = _defaultNameToLocalMap[mapKey];
return localName;
}
}
Usage:
// Creating map:
var map = new MapClass();
map.AddMapping("Pint", LocaleM.UK, "PintUK");
map.AddMapping("Pint", LocaleM.US, "PintUS");
map.AddMapping("Pint", LocaleM.Metric, "Litre");
string ukPintMappedToUS = map.Map("PintUK", LocaleM.US);
UPDATE 2 MapKey equality members generated by resharper:
class MapKey
{
...
public bool Equals(MapKey other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.Locale, Locale) && Equals(other.Key, Key);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (MapKey)) return false;
return Equals((MapKey) obj);
}
public override int GetHashCode()
{
int result = 0;
result = (result * 397) ^ Locale.GetHashCode();
result = (result * 397) ^ (Key != null ? Key.GetHashCode() : 0);
return result;
}
}

Intersect with a custom IEqualityComparer using Linq

Long story short: I have 2 collections of objects. One contains good values (Let's call it "Good"), the other default values (Mr. "Default"). I want the Intersect of the Union between Good and Default, and Default. In other words: Intersect(Union(Good, Default), Default). One might think it resolves as Default, but here is where it gets tricky : I use a custom IEqualityComparer.
I got the following classes :
class MyClass
{
public string MyString1;
public string MyString2;
public string MyString3;
}
class MyEqualityComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass item1, MyClass item2)
{
if(item1 == null && item2 == null)
return true;
else if((item1 != null && item2 == null) ||
(item1 == null && item2 != null))
return false;
return item1.MyString1.Equals(item2.MyString1) &&
item1.MyString2.Equals(item2.MyString2);
}
public int GetHashCode(MyClass item)
{
return new { item.MyString1, item.MyString2 }.GetHashCode();
}
}
Here are the characteristic of my collections Good and Default collections :
Default : It's a large set, containing all the wanted { MyString1, MyString2 } pairs, but the MyString3 values are, as you can guess, default values.
Good : It's a smaller set, containing mostly items which are in the Default set, but with some good MyString3 values. It also has some { MyString1, MyString2 } that are outside of the wanted set.
What I want to do is this : Take only the items from Good that are in Default, but add the other items in Default to that.
Here is, what I think is, my best try :
HalfWantedResult = Good.Union(Default, new MyEqualityComparer());
WantedResult= HalfWantedResult.Intersect(Good, new MyEqualityComparer());
I taught it should have worked, but the result I get is basically only the good { MyString1, MyString2 } pairs set, but all coming from the Default set, so I have the default value all across. I also tried switching the Default and Good of the last Intersect, but I get the same result.
First of all this is wrong:
public bool Equals(MyClass item1, MyClass item2)
{
return GetHashCode(item1) == GetHashCode(item2);
}
If the hashcode's are different for sure the corresponding 2 items are different, but if they're equal is not guaranteed that the corresponding 2 items are equal.
So this is the correct Equals implementation:
public bool Equals(MyClass item1, MyClass item2)
{
if(object.ReferenceEquals(item1, item2))
return true;
if(item1 == null || item2 == null)
return false;
return item1.MyString1.Equals(item2.MyString1) &&
item1.MyString2.Equals(item2.MyString2);
}
As Slacks suggested (anticipating me) the code is the following:
var Default = new List<MyClass>
{
new MyClass{MyString1="A",MyString2="A",MyString3="-"},
new MyClass{MyString1="B",MyString2="B",MyString3="-"},
new MyClass{MyString1="X",MyString2="X",MyString3="-"},
new MyClass{MyString1="Y",MyString2="Y",MyString3="-"},
new MyClass{MyString1="Z",MyString2="Z",MyString3="-"},
};
var Good = new List<MyClass>
{
new MyClass{MyString1="A",MyString2="A",MyString3="+"},
new MyClass{MyString1="B",MyString2="B",MyString3="+"},
new MyClass{MyString1="C",MyString2="C",MyString3="+"},
new MyClass{MyString1="D",MyString2="D",MyString3="+"},
new MyClass{MyString1="E",MyString2="E",MyString3="+"},
};
var wantedResult = Good.Intersect(Default, new MyEqualityComparer())
.Union(Default, new MyEqualityComparer());
// wantedResult:
// A A +
// B B +
// X X -
// Y Y -
// Z Z -
You need to check for actual equality, not just hashcode equality.
GetHashCode() is not (and cannot be) collision free, which is why the Equals method is required in the first place.
Also, you can do this much more simply by writing
WantedResult = Good.Concat(Default).Distinct();
The Distinct method will return the first item of each pair of duplicates, so this will return the desired result.
EDIT: That should be
WantedResult = Good.Intersect(Default, new MyEqualityComparer())
.Union(Default, new MyEqualityComparer());

Categories