How to use a custom comparer for EqualityComparer<T>.Default? - c#

Note: my case is for byte[] but I believe a good answer would work for any type.
Visual Studio's auto-generated implementation of Equals uses EqualityComparer<T>.Default.Equals(T x, T y) for reference types. I have a lot of classes with byte arrays that needs to be included in Equals so I'd like to keep Visual studio's code if possible but Default returns a ObjectEqualityComparer for byte arrays. I've written a simple byte array comparer but I'm not sure how to proceed to have it used instead of ObjectEqualityComparer.
public class Foo
{
public int Id {get;set;}
public byte[] Data {get;set;}
public override bool Equals(object obj)
{
var foo = obj as Foo;
return foo != null &&
Id == foo.Id &&
EqualityComparer<byte[]>.Default.Equals(Data, foo.Data);
}
}
static void Main
{
Foo f1 = new Foo { Id = 1, Data = new byte[1] { 0xFF } };
Foo f2 = new Foo { Id = 1, Data = new byte[1] { 0xFF } };
bool result = f1.Equals(f2); // false
}
public class ByteArrayComparer
{
public bool Equals(byte[] x, byte[] y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(byte[] obj)
{
return obj.GetHashCode();
// as Servy said, this is wrong but it's not the point of the question,
// assume some working implementation
}
}
Should ByteArrayComparer implement IEqualityComparer, inherit from EqualityComparer and override the methods, or something else?

Create and use an instance of your custom comparer instead of using EqualityComparer<byte[]>.Default in your class:
public class Foo
{
public int Id { get; set; }
public byte[] Data { get; set; }
private readonly ByteArrayComparer _comparer = new ByteArrayComparer();
public override bool Equals(object obj)
{
var foo = obj as Foo;
return foo != null &&
Id == foo.Id &&
_comparer.Equals(Data, foo.Data);
}
}
You may also want to implement IEqualityComparer<T> and GetHashCode() in your ByteArrayComparer class. EqualityComparer<T>.Default returns an instance of a class that implements this interface, but I assume you don't want to use this one as you have implemented your own custom comparer.
How to use the IEqualityComparer

Related

How to use different instance of key to retrieve value in a dictionary

I want to use a key (from new instance with the same property) to retrieve value. but it will get KeyNotFoundException.
class Program
{
static void Main(string[] args)
{
Dictionary<Keyclass, ValueClass> dic = new Dictionary<Keyclass, ValueClass>()
{
{ new Keyclass() { Key = "k1" }, new ValueClass() {Value = "v1"} },
{ new Keyclass() { Key = "k2" }, new ValueClass() {Value = "v2"} }
};
var key = new Keyclass() { Key = "k1" };
var value = dic[key];
}
}
public class Keyclass
{
public string Key { get; set; }
}
public class ValueClass
{
public string Value { get; set; }
}
Dictionaries use object.Equals and object.GetHashCode to compare keys so you'll need to implement those in your key class, or provide a IEqualityComparer implementation to the dictionary constructor.
public class Keyclass
{
public string Key { get; set; }
public override bool Equals(object other)
{
var otherKeyClass = other as Keyclass;
return (otherKeyClass != null) && (otherKeyClass.Key == Key);
}
public override int GetHashCode()
{
return Key.GetHashCode();
}
}
As KeyClass is a class, the key is not found, as you create a new object (which has a different reference), even though their properties are the same. Now there are several options:
Overwrite .Equals for the KeyClass, so your two object instances are treated the same and the key can be found.
Instead of creating a new KeyClass instance, get it from the key collection:
var key = dic.Keys.SingleOrDefault(p => p.Key == "k1");
Define KeyClass as a struct instead of class, if possible. Structs are considered equal when all properties are equal.
First of all why aren't you using Dictionary<string,string> instead of using wrapped string ?
Second if you really want to use your wrapper class you have to tell the wrapper class how to compare 2 instances of it by overriding Equals(Keyclass obj) and GetHashCode() Methods :
public override bool Equals(object obj)
{
return this.Key == ((KeyClass)obj).Key;
}
public override int GetHashCode()
{
int hash = 17;
hash = hash * 23 + Key.GetHashCode();
return hash;
}

Linq distinct doesn't call Equals method

I have the following class
public class ModInfo : IEquatable<ModInfo>
{
public int ID { get; set; }
public string MD5 { get; set; }
public bool Equals(ModInfo other)
{
return other.MD5.Equals(MD5);
}
public override int GetHashCode()
{
return MD5.GetHashCode();
}
}
I load some data into a list of that class using a method like this:
public void ReloadEverything() {
var beforeSort = new List<ModInfo>();
// Bunch of loading from local sqlite database.
// not included since it's reload boring to look at
var modinfo = beforeSort.OrderBy(m => m.ID).AsEnumerable().Distinct().ToList();
}
Problem is the Distinct() call doesn't seem to do it's job. There are still objects which are equals each other.
Acording to this article: https://msdn.microsoft.com/en-us/library/vstudio/bb348436%28v=vs.100%29.aspx
that is how you are supposed to make distinct work, however it doesn't seem to be calling to Equals method on the ModInfo object.
What could be causing this to happen?
Example values:
modinfo[0]: id=2069, MD5 =0AAEBF5D2937BDF78CB65807C0DC047C
modinfo[1]: id=2208, MD5 = 0AAEBF5D2937BDF78CB65807C0DC047C
I don't care which value gets chosen, they are likely to be the same anyway since the md5 value is the same.
You also need to override Object.Equals, not just implement IEquatable.
If you add this to your class:
public override bool Equals(object other)
{
ModInfo mod = other as ModInfo;
if (mod != null)
return Equals(mod);
return false;
}
It should work.
See this article for more info: Implementing IEquatable Properly
EDIT: Okay, here's a slightly different implementation based on best practices with GetHashCode.
public class ModInfo : IEquatable<ModInfo>
{
public int ID { get; set; }
public string MD5 { get; set; }
public bool Equals(ModInfo other)
{
if (other == null) return false;
return (this.MD5.Equals(other.MD5));
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + MD5.GetHashCode();
return hash;
}
}
public override bool Equals(object obj)
{
ModInfo other = obj as ModInfo;
if (other != null)
{
return Equals(other);
}
else
{
return false;
}
}
}
You can verify it:
ModInfo mod1 = new ModInfo {ID = 1, MD5 = "0AAEBF5D2937BDF78CB65807C0DC047C"};
ModInfo mod2 = new ModInfo {ID = 2, MD5 = "0AAEBF5D2937BDF78CB65807C0DC047C"};
// You should get true here
bool areEqual = mod1.Equals(mod2);
List<ModInfo> mods = new List<ModInfo> {mod1, mod2};
// You should get 1 result here
mods = mods.Distinct().ToList();
What's with those specific numbers in GetHashCode?
Add
public bool Equals(object other)
{
return this.Equals(other as ModInfo)
}
Also see here the recommendations how to implement the equality members: https://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx

Non-unique elements for custom datatypes

I am trying to get list of unique elements for a custom datatype. I seriously couldn't figure out why this doesn't work. The control never reaches the Equals implementation in the below code. Could someone please help with this?
public class customobj : IEqualityComparer<customobj>
{
public string str1;
public string str2;
public customobj(string s1, string s2)
{
this.str1 = s1; this.str2 = s2;
}
public bool Equals(customobj obj1, customobj obj2)
{
if ((obj1 == null) || (obj2 == null))
{
return false;
}
return ((obj1.str1.Equals(obj2.str1)) && (obj2.str2.Equals(obj2.str2)));
}
public int GetHashCode(customobj w)
{
if (w != null)
{
return ((w.str1.GetHashCode()) ^ (w.str2.GetHashCode()));
}
return 0;
}
}
And below is the part where i am trying to retrieve distinct elements of list.
List<customobj> templist = new List<customobj> { };
templist.Add(new customobj("10", "50"));
templist.Add(new customobj("10", "50"));
List<customobj> dist = templist.Distinct().ToList();
Your class does not override base Equals() from object class, and Distinct() is using it.
Try overriding base Equals, and calling your custom Equals(Rectangle obj1, Rectangle obj2) from there.
Also, if you want to inherit from typed comparer, use IEquatable<T> , but not IEqualityComparer<Rectangle>
bool Equals(Rectangle obj1, Rectangle obj2)
is a static method of Object, so it can't be overridden.
You must override the instance Equals instead.
public override bool Equals(Object obj) {
...
}
If you want to implement IEqualityComparer in Rectangle's class you should to write something this:
List<Rectangle> dist = templist.Distinct(new Reclangle("","")).ToList();
Usually it is implements through an RectangleComparer class:
class RectangleComparer : IEqualityComparer<Rectangle>
{
public static IEqualityComparer<Rectangle> Instance { get {...} }
...
}
List<Rectangle> dist = templist.Distinct(RectangleComparer.Instance).ToList();
Or override GetHashCode and Equals =)
You are implementing the wrong interface. Your class implements IEqualityComparer<Rectangle>, not IEquatable<Rectangle>. Unless you pass in an IEqualityComparer to Distinct, it will use either IEquatable.Equals (if youe class implements it) or Object.Equals.
public class Rectangle : IEquatable<Rectangle>
{
public string width;
public string height;
public Rectangle(string s1, string s2)
{
this.width = s1; this.height = s2;
}
`IEquatable.Equals
public bool Equals(Rectangle obj2)
{
if (obj2 == null)
{
return false;
}
return ((this.width.Equals(obj2.width)) && (this.height.Equals(obj2.height)));
}
`override of object.Equals
public override bool Equals(Object(o2)
{
if(typeof(o2) == typeof(Rectangle))
return ((Rectangle)this.Equals((Rectangle)o2);
return false;
}
'override of object.GetHashCode
public override int GetHashCode()
{
return ((this.width.GetHashCode()) ^ (thisw.height.GetHashCode()));
}
}
Also, is there a particular reason why your width and height are strings and not numeric types? It seems very odd, and could lead to weird bugs such as assuming that "100" and "0100" and " 100 " are equal, when in fact they are distinct strings and will have different hash codes.

C# Linq, Searching for same items in two lists

we have the following setup:
We have a array of objects with a string in it (xml-ish but not normalized) and we have a list/array of strings with id.
We need to find out if a string from that list with id's is also pressent in one of the objects.
Here we have a setup that we have tried:
public class Wrapper
{
public string MyProperty { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Wrapper> wrappers = new List<Wrapper>()
{
new Wrapper{ MyProperty = "<flkds,dlsklkdlsqkdkqslkdlqk><id>3</id><sqjldkjlfdskjlkfjsdklfj>"},
new Wrapper{ MyProperty = "<flkds,dlsklkdlsqkdkqslkdlqk><id>2</id><sqjldkjlfdskjlkfjsdklfj>"}
};
string[] ids = { "<id>0</id>", "<id>1</id>", "<id>2</id>" };
var props = wrappers.Select(w => w.MyProperty);
var intersect = props.Intersect(ids, new MyEquilityTester());
Debugger.Break();
}
}
class MyEquilityTester: IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return x.Contains(y);
}
public int GetHashCode(string obj)
{
return obj.GetHashCode();
}
}
Edit:
What we expect is when we do a .Any() on intersect that is says true because wrappers has a object with a prop that contains <id>2</id>, intersect is null.
If we are using the wrong method please say. It should work as fast as posible. A simple true when found will do!
For your case, you could write your IEqualitycomparer like this:
class MyEquilityTester: IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return x.Contains(y) || y.Contains(x);
}
public int GetHashCode(string obj)
{
return 0;
}
}
and it will find
<flkds,dlsklkdlsqkdkqslkdlqk><id>2</id><sqjldkjlfdskjlkfjsdklfj>
This works because GetHashCode always return 0, and the x.Contains(y) || y.Contains(x) check.
Another not-so-hacky solution is to use a Where in combination with Any
IEnumerable<String> intersect = props.Where(p => ids.Any (i => p.Contains(i)));
or replace the Where with another Any if you don't care about the actual items and you only want a true or false.
bool intersect = props.Any(p => ids.Any (i => p.Contains(i)));
wrappers.Where(w=>ids.Any(i=>w.MyProperty.Contains(i)))

Linq .Except function "At least one object must implement IComparable."

Basically i have a container which implements IEquatable (sample shown below)
public class ContainerClass : IEquatable<ContainerClass>
{
public IEnumerable<CustomClass> CustomClass { get; set; }
public override bool Equals(object obj) { ... }
public bool Equals(ContainerClass other) { ... }
public static bool operator ==(ContainerClass cc1, ContainerClass cc2) { ... }
public static bool operator !=(ContainerClass cc1, ContainerClass cc2) { ... }
public override int GetHashCode() { ... }
}
and a CustomClass which also implements IEquatable
public class CustomClass : IEquatable<CustomClass>
{
public string stringone { get; set; }
public string stringtwo { get; set; }
public override bool Equals(object obj) { ... }
public bool Equals(CustomClass other) { ... }
public static bool operator ==(CustomClass cc1, CustomClass cc2) { ... }
public static bool operator !=(CustomClass cc1, CustomClass cc2) { ... }
public override int GetHashCode() { ... }
}
All this is working fine, so for example, the following works
IEnumerable<CustomClass> customclassone = new List<CustomClass>
{
new CustomClass { stringone = "hi" },
new CustomClass { stringone = "lo" }
};
IEnumerable<CustomClass> customclasstwo = new List<CustomClass>
{
new CustomClass { stringone = "hi" }
};
var diff = customclassone.Except(customclasstwo);
ContainerClass containerclassone = new ContainerClass
{
CustomClass = customclassone.AsEnumerable()
};
ContainerClass containerclasstwo = new ContainerClass
{
CustomClass = customclasstwo.AsEnumerable()
};
var diff2 = containerclassone.CustomClass.Except(customclasstwo.CustomClass);
After this code both diff and diff2 when enumerated contain the expected results. However, if i then try
IEnumerable<CustomClass> oldCustom = oldContainerClass.CustomClass;
IEnumerable<CustomClass> newcustom = newContainerClass.CustomClass;
var exceptlist = oldCustom.Except(newcustom);
When i try to enumerate the exceptlist i get "At least one object must implement IComparable.". The only difference between oldCustom and newCustom from the ones in the above working examples is the way they are populated. Anyone got any idea why this is happening?
I suspect that you attempted to sort these contents of the ContainerClass.CustomClass. Due to the deferred execution, you don't know there's a problem until you iterate through it and Except() is just a red herring. CustomClass doesn't implement the IComparable interface so the sort fails with that error. Your CustomClass should either implement the IComparable<T> interface or you should pass in an IComparer on your OrderBy().
e.g.,
oldContainerClass.CustomClass = someListOfSomeType.OrderBy(x => x.CustomClasss, myComparer)
.Select(x => x.CustomClass);
Though it would help to see what exactly you assigned to these properties so we can give you a more precise reason.

Categories