I have this simple example:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Dictionary<MyKey, string> data = new Dictionary<MyKey, string>();
data.Add(new MyKey("1", "A"), "value 1A");
data.Add(new MyKey("2", "A"), "value 2A");
data.Add(new MyKey("1", "Z"), "value 1Z");
data.Add(new MyKey("3", "A"), "value 3A");
string myValue;
if (data.TryGetValue(new MyKey("1", "A"), out myValue))
Console.WriteLine("I have found it: {0}", myValue );
}
}
public struct MyKey
{
private string row;
private string col;
public string Row { get { return row; } set { row = value; } }
public string Column { get { return col; } set { col = value; } }
public MyKey(string r, string c)
{
row = r;
col = c;
}
}
}
This is working fine. But if I change the MyKey struct by a MyKey class in this way:
public class MyKey
Then method TryGetValue doesn't find any key in spite of the key is out there.
I am sure I am missing something obvious but I don't know what.
Any idea ?
Thanks
** Solution **
(please, see accepted solution for better GetHashCode resolution)
I have redefined MyKey class like this, and all is working fine now:
public class MyKey
{
private string row;
private string col;
public string Row { get { return row; } set { row = value; } }
public string Column { get { return col; } set { col = value; } }
public MyKey(string r, string c)
{
row = r;
col = c;
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is MyKey)) return false;
return ((MyKey)obj).Row == this.Row && ((MyKey)obj).Column == this.Column;
}
public override int GetHashCode()
{
return (this.Row + this.Column).GetHashCode();
}
}
Thanks to all people answered this.
You need to override Equals() and GetHashCode() in the class MyKey
Maybe something like this:
GetHashCode()
public override int GetHashCode()
{
return GetHashCodeInternal(Row.GetHashCode(),Column.GetHashCode());
}
//this function should be move so you can reuse it
private static int GetHashCodeInternal(int key1, int key2)
{
unchecked
{
//Seed
var num = 0x7e53a269;
//Key 1
num = (-1521134295 * num) + key1;
num += (num << 10);
num ^= (num >> 6);
//Key 2
num = ((-1521134295 * num) + key2);
num += (num << 10);
num ^= (num >> 6);
return num;
}
}
Equals
public override bool Equals(object obj)
{
if (obj == null)
return false;
MyKey p = obj as MyKey;
if (p == null)
return false;
// Return true if the fields match:
return (Row == p.Row) && (Column == p.Column);
}
Because classes are compared by default using reference comparison.
If you compare two objects you are doing a object.ReferenceEquals(obj1, obj2)
If you compare two structs you are doing a value comparison (like when you compare two ints for example).
If you want to compare two MyKey objects you need to implement you own Equals and GetHashCode method and it will be used automatically by the dictionary.
Struct is value type and Class is reference type, so when you use struct all values inside it are compared but when you use class instead only the object reference is checked.
You can change that behavior for certain classes by overriding Equals() method. You can also override == operator if you want. See samples on Guidelines for Overloading Equals() and Operator == (C# Programming Guide).
Edit:
your Equals() method should look like that:
public override bool Equals(System.Object obj)
{
MyKey p = obj as MyKey;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (row == p.row) && (col == p.col);
}
Related
I've this class:
public class Pair<T, V>
{
public T A = default;
public V B = default;
public Pair()
{
A = default;
B = default;
}
public Pair(T a, V b)
{
A = a;
B = b;
}
public override bool Equals(object obj)
{
Pair<T, V> other = obj as Pair<T, V>;
return A.Equals(other.A) && B.Equals(other.B);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override string ToString()
{
return "Pair: (" + A.ToString() + " , " + B.ToString() + ")";
}
}
And I have a class with two Pair variables:
public class FakeClass<T>
{
public T LastValue { get; protected set; } = default;
public T CurrentValue = default;
public void Execute()
{
LastValue = CurrentValue
}
}
public class FakeClassWithPair : FakeClass<Pair<int, int>> { }
Now if I execute this code:
FakeClassWithPair fake = new FakeClassWithPair();
fake.CurrentValue.A = 2;
fake.CurrentValue.B = 5;
fake.Execute();
fake.CurrentValue.A = 32;
fake.CurrentValue.B = 53;
In debugging Current Value and Last Value have the same value "32" and "53".
How can I avoid this?
Classes are reference types, so when you set LastValue = CurrentValue, that means both LastValue and CurrentValue refer to the same object.
If you want Value semantics you should declare your Pair as a struct. This means that an assignment does a copy of the value. Except ofc there already are a built in type for this: ValueTuple, with some special syntax that lets you declare types like (int A, int B). There is also a regular Tuple<T1, T2> if you do want a reference type.
Also note that I see no way for your example to run, fake.CurrentValue should be initialized to null and crash when accessed. Using a value type would also solve this, since they cannot be null.
So just change your example to FakeClassWithPair:FakeClass<(int A, int B)> and everything should work as you expect it to.
Definitely do not roll your own class for a pair if you want value semantics. Use the built-in value tuple, defined as (T a, V b).
Also if your content of FakeClass is cloneable then you should take advantage of that (for example arrays are cloneable). So the assignment in Execute() would check if the current value implements ICloneable and proceeds accordingly.
See this example code with output. The first example with fk variable is defined by FakeClass<(int,int)> and the second example with fa variable is defined by FakeClass<int[]>. Some fun code is added to display arrays as list of vales in ToString() in order to mimic the behavior of tuples with arrays.
public class FakeClass<T>
{
public T LastValue { get; protected set; } = default(T);
public T CurrentValue = default(T);
public void Execute()
{
if (CurrentValue is ICloneable cloneable)
{
LastValue = (T)cloneable.Clone();
}
else
{
LastValue = CurrentValue;
}
}
public override string ToString()
{
if (typeof(T).IsArray)
{
object[] last, current;
Array cv = CurrentValue as Array;
if (cv != null)
{
current = new object[cv.Length];
cv.CopyTo(current, 0);
}
else
{
current = new object[0];
}
Array lv = LastValue as Array;
if (lv != null)
{
last = new object[lv.Length];
lv.CopyTo(last, 0);
}
else
{
last = new object[0];
}
return $"Current=[{string.Join(",",current)}], Last=[{string.Join(",",last)}]";
}
return $"Current={CurrentValue}, Last={LastValue}";
}
}
class Program
{
static void Main(string[] args)
{
var fk = new FakeClass<(int a, int b)>();
fk.CurrentValue = (1, 2);
Console.WriteLine(fk);
// Current=(1, 2), Last=(0, 0)
fk.Execute();
fk.CurrentValue = (3, 4);
Console.WriteLine(fk);
// Current=(3, 4), Last=(1, 2)
var fa = new FakeClass<int[]>();
fa.CurrentValue = new int[] { 1, 2 };
Console.WriteLine(fa);
//Current=[1,2], Last=[]
fa.Execute();
fa.CurrentValue = new int[] { 3, 4 };
Console.WriteLine(fa);
//Current=[3,4], Last=[1,2]
}
}
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 }
}
}
}
The following prints equals:
struct A
{
int x;
public A(int _x) { x = _x; }
public int Y
{
get
{
Random r = new Random();
return r.Next(0, 1000);
}
}
}
static void Main(string[] args)
{
A a1 = new A(1),a2 = new A(1);
if (a1.Equals(a2))
{
Console.Write("Equals");
}
else
{
Console.Write("Different");
}
}
Is there anyway to get C# to return false in that case? Meaning, to take the properties under consideration when comparing value types?
Write equal then hit "tab" button twice:
// override object.Equals
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
// TODO: write your implementation of Equals() here
throw new NotImplementedException();
return base.Equals(obj);
}
This is an automatically generated snippet. Now you can try something like:
// override object.Equals
public override bool Equals(object obj)
{
// checks for A versus A
if (obj == null || GetType() != obj.GetType())
{
return false;
}
// TODO: write your implementation of Equals() here
throw new NotImplementedException();
int compareThis=(A)obj.x;
return ((A)base).x==compareThis; // maybe casting is not needed
}
The recommended approach is to use IEquatable<T> instead of using the default inherited Equals method. The IEquatable generic interface defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.
using System;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
A a1 = new A(1), a2 = new A(1);
//here the CLR will do a lot of unboxing and check operations via reflection in order to make a comparaison between fields value.
//just take a look bellow at the decompiled default Equals method how it's done
if (a1.Equals(a2))
{
Console.Write("Equals");
}
else
{
Console.Write("Different");
}
}
}
public struct A : IEquatable<A>
{
int x;
public A(int _x) { x = _x; }
public int Y
{
get
{
Random r = new Random();
return r.Next(0, 1000);
}
}
//here no boxing or unboxing is needed even if is a value type and the CLR will call this method first
public bool Equals(A other)
{
return this.Y == other.Y;
}
public override bool Equals(object obj)
{
//this is why a bad approach to compare both objects you need to unbox the struct arguments wich hurting performance
return this.Y == ((A)obj).Y;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
//default implementation
//public override bool Equals(object obj)
//{
// return base.Equals(obj);
//}
}
}
CLR Implementation
The CLR what's going underneath
public override bool Equals(object obj)
{
if (obj == null)
return false;
RuntimeType runtimeType = (RuntimeType) this.GetType();
if ((RuntimeType) obj.GetType() != runtimeType)
return false;
object a = (object) this;
if (ValueType.CanCompareBits((object) this))
return ValueType.FastEqualsCheck(a, obj);
FieldInfo[] fields = runtimeType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int index = 0; index < fields.Length; ++index)
{
object obj1 = ((RtFieldInfo) fields[index]).UnsafeGetValue(a);
object obj2 = ((RtFieldInfo) fields[index]).UnsafeGetValue(obj);
if (obj1 == null)
{
if (obj2 != null)
return false;
}
else if (!obj1.Equals(obj2))
return false;
}
return true;
}
This is very similar to this question.
All you need to do is to override the Equals method:
struct A
{
int x;
public A(int _x) { x = _x; }
public int Y
{
get
{
Random r = new Random();
return r.Next(0, 1000);
}
}
public override bool Equals(object obj)
{
//compare whatever you want...
}
}
}
Override equal method and return true or false based on property comparison.
In my programm i have very big(many-many terrabytes) stream of data as input, from which i need to get key - byte array(exacly 16 bytes - some pseudo-GUID), and by that key i need to get some other key - integer, and by that second key i need to do some calculations in memory. But i don't know how to get this "second key" really fast..
I can get all "byte[16] -> int" associations before start to work with this "very big stream of data".
Dictionary seems to be pretty fast, but it don't work right with byte[16] as key - it's just check for object reference equality(in my sitiuation it will be always equal, because it's just buffer object), but i need use real byte-array values as key, and do this fast(every second counts)... How to do this?
Well... I'm already trying to implent own associative array class, based on binary search.. Or there any standart alternative to Dictionary, which can use something bigger than 4-byte int as key?
With the generic Dictionary, you can supply your own IEqualityComparer comparer. You can use this class to compare the keys.
This will get you started, performance can probably be optimized:
public class MyComparer : IEqualityComparer<byte[]> {
public bool Equals(byte[] a, byte[] b) {
return a.SequenceEqual(b);
}
public int GetHashCode(byte[] key) {
if (key == null)
throw new ArgumentNullException("key");
return key.Sum(b => b);
}
}
// usage:
myDict = new Dictionary(myByteArray, myInt, new MyComparer());
Alternatively, you could write some code to represent your type, as below.
public struct PseudoGuid : IReadOnlyList<byte>, IEquatable<PseudoGuid>
{
private readonly byte[] value;
public PseudoGuid(IList<byte> value)
{
if (value.Count != 16)
{
throw new ArgumentException(...
}
this.value = value.ToArray();
}
public byte this[int index]
{
get
{
if (this.value != null)
{
return this.value[index]
}
return default(byte);
}
}
public bool Equals(PseudoGuid other)
{
return this.SequenceEquals(other);
}
public override string ToString()
{
var result = new StringBuilder(32);
for (var i = 0; i < 16; i++)
{
result.Append(this[i].ToString("X2"));
}
return result.ToString();
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is PseudoGuid && this.Equals((PseudoGuid)obj);
}
public override int GetHashCode()
{
if (this.value == null)
{
return 0;
}
return BitConvertor.ToInt32(this.value, 0) ^
BitConvertor.ToInt32(this.value, 4) ^
BitConvertor.ToInt32(this.value, 8) ^
BitConvertor.ToInt32(this.value, 12);
}
public static bool operator ==(PseudoGuid left, PseudoGuid right)
{
return left.Equals(right);
}
public static bool operator !=(PseudoGuid left, PseudoGuid right)
{
return !left.Equals(right);
}
public int Count
{
get
{
return 16;
}
}
public IEnumerator<byte> GetEnumerator()
{
if (this.value != null)
{
return ((IList<byte>)this.value).GetEnumerator();
}
return Enumerable.Repeat(default(byte), 16).GetEnumerator();
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
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!