I'm getting the first key in a dictionary and comparing it with a lookup value. They are the same, but when I try to access the key from the dictionary I get a KeyNotFoundException:
public void Draw(Graphics graphics, Shape shape)
{
foreach (var line in shape.Lines)
{
var tuple = shape.LinesToNurmalDictionary.Keys.First();
bool equals = tuple == line;
bool referenceEquals = ReferenceEquals(tuple, line);
var invisible = shape.LinesToNurmalDictionary[line].All(x => x.Z < 0);
How can I fix it?
Added:
Dictionary is
Dictionary<Tuple<ShapePoint, ShapePoint>, List<ShapePoint>> LinesToNurmalDictionary;
where ShapePoint is a class
So i 'solved' my problem by using my own class instead of Tuple, but actualy question still unresponsed:
public class Line
{
public ShapePoint A { get; set; }
public ShapePoint B { get; set; }
public List<ShapePoint> Normals { get; set; }
public Line(ShapePoint a, ShapePoint b)
{
A = a;
B = b;
Normals = new List<ShapePoint>();
}
private sealed class DefaultEqualityComparer : IEqualityComparer<Line>
{
public bool Equals(Line x, Line y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null))
return false;
if (ReferenceEquals(y, null))
return false;
if (x.GetType() != y.GetType())
return false;
return Equals(x.A, y.A) && Equals(x.B, y.B);
}
public int GetHashCode(Line obj)
{
unchecked
{
return ((obj.A != null ? obj.A.GetHashCode() : 0)*397) ^ (obj.B != null ? obj.B.GetHashCode() : 0);
}
}
}
private static readonly IEqualityComparer<Line> DefaultComparerInstance = new DefaultEqualityComparer();
public static IEqualityComparer<Line> DefaultComparer
{
get
{
return DefaultComparerInstance;
}
}
}
As tia already said in a comment, this happens because an entry is first added to the Dictionary<,> where the hash code of the key (here the Tuple<ShapePoint, ShapePoint>) has some value which is "saved" and kept by the Dictionary<,>. After that the key object is "mutated" (modified) in a way that changes its hash code. This leads to the Dictionary<,> being in a corrupt state.
Therefore when the key is searched for afterwards, it is not found. Its "new" hash code differs from the hash the Dictionary<,> thinks this key has.
Do not modify objects that might be keys in a Dictionary<,> (or members of a HashSet<> and so on) in a way that changes their hash codes.
Here's an example illustrating. The type:
class Changeable
{
public int Prop { get; set; }
public override int GetHashCode()
{
return Prop;
}
public override bool Equals(object obj)
{
return Equals(obj as Changeable);
}
public bool Equals(Changeable other)
{
if (other == null)
return false;
return GetType() == other.GetType() && Prop == other.Prop;
}
}
The code that gives the same problem:
var onlyInstance = new Changeable();
var dict = new Dictionary<Changeable, string>();
onlyInstance.Prop = 1;
dict.Add(onlyInstance, "what-ever");
onlyInstance.Prop = 2;
string restoredValue = dict[onlyInstance]; // throws!
Related
I have many entities that need to share data between themselves. Both entities will request values from this dictionary.
public class Key {
public string nameA;
public string nameB;
}
public class SharedValue {
public int id;
}
private Dictionary<Key, SharedValue> relation = new Dictionary<Key, SharedValue>();
Then adding to the dictionary.
relation.Add(new Key(){ nameA = "User1", nameB = "User2" }, new SharedValue(){ id = -11 });
Finally I was hoping I could get a shared SharedValue no matter of the order of nameA or nameB.
relation[new Key(){ nameA = "User1", nameB = "User2" }].id // Get -11
relation[new Key(){ nameA = "User2", nameB = "User1" }].id // Get -11
Define Equals and GeyHashCode methods for your key class:
public class Key
{
public string nameA;
public string nameB;
public override bool Equals(object obj)
{
return base.Equals(obj as Key);
}
protected bool Equals(Key other)
{
return string.Equals(nameA, other.nameA) && string.Equals(nameB, other.nameB) ||
string.Equals(nameA, other.nameB) && string.Equals(nameB, other.nameA);
}
public override int GetHashCode()
{
return (nameA?.GetHashCode() ^ nameB?.GetHashCode()) ?? 0;
}
}
You could provide a cutom IEqualityComparer<Key> which you can use for the dictionary constructor (and many LINQ methods as well):
public class UnorderedKeyComparer : IEqualityComparer<Key>
{
public bool Equals(Key x, Key y)
{
if (object.ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
if (string.Equals(x.NameA, y.NameA) && string.Equals(x.NameB, y.NameB))
return true;
if (string.Equals(x.NameA, y.NameB) && string.Equals(x.NameB, y.NameA))
return true;
return false;
}
public int GetHashCode(Key obj)
{
return (obj?.NameA?.GetHashCode() ?? int.MinValue) ^ (obj?.NameB?.GetHashCode() ?? int.MinValue);
}
}
So in this case you just need to initialize the dictionary with the UnorderedKeyComparer:
Dictionary<Key, SharedValue> Relation = new Dictionary<Key, SharedValue>(new UnorderedKeyComparer());
By the way, i couldn't resist to fix your naming issues.
You have many options, examples are:
Implement IEqualityComparer<YourKey> and pass an instance of a
class implements IEqualityComparer<YourKey> to the constructor of the
dictionary
Implement IEquatable<T> on your custom key only.
This is how to do Option 2:
public class Key:IEquatable<Key> {
public string nameA;
public string nameB;
public bool Equals(Key other)
{
if(other == null)
return false;
if(ReferenceEquals(this,other))
return true;
return (string.Equals(this.nameA,other.nameA) && string.Equals(this.nameB,other.nameB))
|| (string.Equals(this.nameA,other.nameB) && string.Equals(this.nameB,other.nameA));
}
public override bool Equals(object obj){
if(obj == null)
return false;
if(ReferenceEquals(obj,this))
return true;
return Equals((Key)obj);
}
public override int GetHashCode(){
return (nameA?.GetHashCode() ^ nameB?.GetHashCode()) ?? 0;
}
}
Testing:
private static Dictionary<Key, SharedValue> relation = new Dictionary<Key, SharedValue>();
static void Main(string[] args)
{
relation.Add(new Key{nameA="a",nameB="b"},new SharedValue{id=1});
if(relation.ContainsKey(new Key{nameA="a",nameB="b"})){
Console.WriteLine("YES");
}
if(relation.ContainsKey(new Key{nameA="b",nameB="a"})){
Console.WriteLine("YES");
}
else Console.WriteLine("NO");
}
Output:
YES
YES
I have a dictionary:
Dictionary<HashSet<myClass>, List<MyObj>> myDict = ...
And I have:
HashSet<myClass> myHashSet = ...
I want to check if the dictionary (myDict) contains myHashSet.
I tried to override two methods:
1) equal
2) GetHashCode
public class myClass
{
public string id;
public int number;
public override bool Equals(object obj)
{
myClass other = obj as myClass;
bool ret = false;
if (other != null)
{
ret = (this.number == other.number) && (this.id == other.id);
}
return ret;
}
public override int GetHashCode()
{
return this.number ^ this.id.GetHashCode();
}
};
Unfortunately, a key that is found in dictionary, returns false for the code:
myDict.ContainsKey(myHashSet)
Any help appreciated!
Just because you overrode myClass's Equals( and GetHashCode() does not mean that that you overrode HashSet<myClass>'s Equals( and GetHashCode(), that is what is being used when you do the dictionary lookup.
If you want it to work you need to pass a IEqualityComparer<HashSet<myClass>> in to the constructor of the dictionary so it will use that comparer when doing the dictionary lookup.
public class myClassSetComperer : IEqualityComparer<HashSet<myClass>>
{
public bool Equals(HashSet<myClass> x, HashSet<myClass> y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(null, x)) return false;
if (ReferenceEquals(null, y)) return false;
return x.SetEquals(y);
}
public int GetHashCode(HashSet<myClass> obj)
{
unchecked
{
int x = 0;
foreach (var myClass in obj)
{
x = (x*397) ^ myClass?.GetHashCode() ?? 0;
}
return x;
}
}
}
//elsewhere
Dictionary<HashSet<myClass>, List<MyObj>> myDict = new Dictionary<HashSet<myClass>, List<MyObj>>(new myClassSetComperer());
VERY IMPORTANT NOTE: Dictionary keys (and hash sets) break horribly if you do anything that causes Equals( or GetHashCode() to change once put in as the lookup key. If you modify the HashSet<myClass> or one of the myClass objects after you put it in the dictionary you will break the dictionary and potentially the HashSet. See this very good blog post by Eric Lippert on "Guidelines and rules for GetHashCode"
overriding the GetHasCode and Equal is when comparing instances of myClass.
Here's a sample of using ContainsKey, which is checking by objects reference.
Dictionary<HashSet<string>, List<string>> hashSetDictionary = new Dictionary<HashSet<string>, List<string>>();
var myHashSet = new HashSet<string>();
hashSetDictionary.Add(myHashSet, null);
Console.WriteLine(hashSetDictionary.ContainsKey(myHashSet));
Here's an update to your code
public class myClass
{
public myClass(string text, int num)
{
this.Text = text;
this.Num = num;
}
public string Text { get; set; }
public int Num { get; set; }
}
public class MyObj { }
public class AlwaysTrueHashSet<T> : HashSet<T>
{
public override bool Equals(object obj)
{
return this.GetHashCode() == obj.GetHashCode();
}
public override int GetHashCode()
{
return "Counting hashcode".GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
Dictionary<HashSet<myClass>, List<MyObj>> myDict = new Dictionary<HashSet<myClass>,
List<MyObj>>();
var myHashSet1 = new AlwaysTrueHashSet<myClass>();
myHashSet1.Add(new myClass("123", 5));
myDict.Add(myHashSet1, null);
var myHashSet2 = new AlwaysTrueHashSet<myClass>();
myHashSet2.Add(new myClass("123", 5));
/*
* when containsKey is invoked, it's checking if the reference of myHashSet2 is the same as myHashSet1.
* That's the default behavior.
*
* extend HashSet, and override the gethashcode and equal methods
*/
if (myDict.ContainsKey(myHashSet2))
{
Console.WriteLine("in");
int i = 3; // it doesn't get this line }
}
}
}
This question already has answers here:
How does HashSet compare elements for equality?
(5 answers)
Closed 7 years ago.
i don't know how but for weeks now i was using a HashSet<myObject> collection of mainly strings members as i really though it's internally uses a builtin approach as dictionary to avoid duplicate items in a non KVP format of data(single columns)
my scenario is :
HashSet<myHddFolderPaths> UniqColleciton = new HashSet<myHddFolderPtahs>
int countRounds=0;
void addToCollection()
{
for(int i=0, i < UniqColleciton.Count; i++)
{
add some items to UniqColleciton via Directory.GetDirectories();
}
if(countRounds++ < Limit)
addToCollection()
}
this is a pattern for dir-walker i am building and this is only an example for a scenario when recurse of same data could not be avoided so i don't recall where i have read about it and thought, that by simply using a HashSet<T> would "just take Care of business"
i haven't thought of the option it will allow duplicates but in this project i put it to a test and it did allow to my surprise to add an existing items
so my work around is :
Dictionary<string, int> fiterAccesDeniedPaths = new Dictionary<string, int>();
Dictionary<string, int> fiterAccesiblePaths = new Dictionary<string, int>();
if (this.fiterAccesDeniedPaths.ContainsKey(e.Message)) continue;
if (this.fiterAccessiblePaths.ContainsKey(object.stringPathMember)) continue;
add to filters ; UniqColleciton.Add(myHddFolderPaths);
is there a better/more efficient approach for acomplishing this task ?
public class FolderPath
{
public string DriveL { get; set; }
public string FolderLevel { get; set; }
public string Path { get; set; }
public int Fsize { get; set; }
}
public class GenericUniqCollectionM<T> : HashSet<T>
{
public GenericUniqCollectionM():base()
{
}
}
A HashSet created with the parameterless constructor uses a default equality comparer. The default comparer will use FolderPath.Equals() to check equality.
internal class ObjectEqualityComparer<T> : EqualityComparer<T>
{
public override bool Equals(T x, T y)
{
if (x != null)
{
if (y != null) return x.Equals(y);
return false;
}
if (y != null) return false;
return true;
}
public override int GetHashCode(T obj)
{
if (obj == null) return 0;
return obj.GetHashCode();
}
...
}
You didn't override Equals and GetHashCode, so it will use the default implementation provided by object, checking reference equality.
You have two options now. One is to override Equals and GetHashCode in FolderPath.
public class FolderPath
{
...
public override bool Equals(object obj)
{
if (obj == null) return false;
FolderPath other = obj as FolderPath;
if (other == null) return false;
//simple implementation, only compares Path
return Path == other.Path;
}
public override int GetHashCode()
{
if (Path == null) return 0;
return Path.GetHashCode();
}
}
The other one is to implement a custom IEqualityComparer<FolderPath>
public class FolderPathComparer : IEqualityComparer<FolderPath>
{
public bool Equals(FolderPath x, FolderPath y)
{
if (x != null)
{
if (y != null) return x.Path == y.Path;
return false;
}
if (y != null) return false;
return true;
}
public int GetHashCode(FolderPath obj)
{
if (obj == null || obj.Path == null) return 0;
return obj.Path.GetHashCode();
}
}
and pass it to the HashSet constructor.
var set = new HashSet<FolderPath>(new FolderPathComparer());
Ypu wanted your HashSet to "take care of business". HashSet does exactly that. But you first have to let it know what you consider a "duplicate" (or rather when your objects should be considered equal). To do so, you should implement (override) GetHashCode() method on your myHddFolderPaths class.
How does HashSet compare elements for equality?
Implementing GetHashCode correctly
Default implementation for Object.GetHashCode()
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
So what I want to do is this
var result = dictionary.ContainsKey(Guid.Empty);
Where dictionary is defined as var dictionary = new Dictionary<FooKeyClass, int>();
Right now FooKeyClass is basically just some data with a public property of type Guid.
I have tried to override Equals, I've tried writing my own IComparer, I've tried to inherit IEquatable<Guid>. What ever I do I can't seem to get the desired functionality. Can someone please tell me if this even is possible in C# and if so how do I go by implementing it?
Here is the rest of the code, its kinda bloted with overrides as it is now though:
public class FooKeyClass : IEquatable<Guid>
{
public Guid Guid { get; set; }
public string Name { get; set; }
public bool Equals(Guid guid)
{
if (guid == null)
return false;
return Guid.Equals(guid);
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
var p = obj as FooKeyClass;
if ((object)p == null)
return false;
return p.Guid.Equals(this.Guid);
}
public static bool operator ==(FooKeyClass a, Guid b)
{
if (object.ReferenceEquals(a, b))
return true;
if (((object)a == null) || ((object)b == null))
return false;
return a.Guid.Equals(b);
}
public static bool operator ==(FooKeyClass a, FooKeyClass b)
{
if (System.Object.ReferenceEquals(a, b))
return true;
if (((object)a == null) || ((object)b == null))
return false;
return a.Guid.Equals(b.Guid);
}
public static bool operator !=(FooKeyClass a, FooKeyClass b)
{
return !(a == b);
}
public static bool operator !=(FooKeyClass a, Guid b)
{
return !(a == b);
}
public override int GetHashCode()
{
return Guid.GetHashCode();
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var dictionary = new Dictionary<FooKeyClass, int>();
var savedGuid = Guid.NewGuid();
var tmpKey = new FooKeyClass() { Guid = savedGuid, Name = "feeefeee" };
dictionary.Add(tmpKey, 42);
var result = tmpKey.Equals(savedGuid); // no error
result = tmpKey == savedGuid; // no error
result = dictionary.ContainsKey(savedGuid); // compile errror
result = dictionary.Contains(savedGuid); // compile errror
result = dictionary.Contains<Guid>(savedGuid); // compile errror
}
}
Your two primary options are the following:
Use the Guid to create an instance of FooKeyClass that you use as the actual key:
var result = dictionary.ContainsKey(new FooKeyClass(Guid.Empty));
Change the type of your dictionary to Dictionary<Guid, int>.
If you frequently use Guid.Empty as the key for some particular purpose, you can create the equivalent key FooKeyClass.Empty so you don't have to keep creating new instances of it.
Note that since your FooKeyClass is used as the key for a dictionary, you should make sure the result of your GetHashCode method cannot change after an instance is created. In your current implementation, if you set the FooKeyClass.Guid property after the key is added to a dictionary, the entry in the dictionary will be "lost" because the hash code changes. This situation is automatically avoided if you use Guid instead of FooKeyClass as the keys of your dictionary, or you can remove the setter for the FooKeyClass.Guid property and require the user to use a constructor instead.