ContainsKey в словаре hashset<myClass>
У меня есть словарь:
Dictionary<HashSet<myClass>, List<MyObj>> myDict = ...
И я имею:
HashSet<myClass> myHashSet = ...
Я хочу проверить, содержит ли словарь (myDict) myHashSet.
Я попытался переопределить два метода:
1) равный
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();
}
};
К сожалению, ключ, найденный в словаре, возвращает false для кода:myDict.ContainsKey(myHashSet)
Любая помощь приветствуется!
2 ответа
Просто потому, что ты переиграл myClass
"s Equals(
а также GetHashCode()
это не значит что ты переиграл HashSet<myClass>
"s Equals(
а также GetHashCode()
это то, что используется при поиске в словаре.
Если вы хотите, чтобы это работало, вам нужно пройти IEqualityComparer<HashSet<myClass>>
в конструктор словаря, чтобы он использовал этот компаратор при поиске в словаре.
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());
ОЧЕНЬ ВАЖНОЕ ПРИМЕЧАНИЕ: словарные ключи (и хэш-наборы) ужасно ломаются, если вы делаете что-либо, что вызывает Equals(
или же GetHashCode()
изменить один раз, вставив в качестве ключа поиска. Если вы измените HashSet<myClass>
или один из myClass
Объекты после того, как вы поместите его в словарь, вы сломаете словарь и, возможно, HashSet. Посмотрите этот очень хороший пост Эрика Липперта в блоге "Руководства и правила для GetHashCode"
Переопределение GetHasCode и Equal происходит при сравнении экземпляров myClass.
Вот пример использования ContainsKey, который проверяется по ссылке на объекты.
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));
Вот обновление вашего кода
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 }
}
}
}