Словарь С#, который использует неупорядоченную пару в качестве ключа?
Я пытаюсь создать словарь C#, который использует 9X_dict неупорядоченную пару индексов в качестве 9X_unity5 ключа.
Например:
exampleDictionary[new UnorderedPair(x,y)]
и exampleDictionary[new UnorderedPair(y,x)]
должны возвращать одно 9X_c# и то же значение.
Есть ли способ создать 9X_unordered пользовательскую неупорядоченную коллекцию, кроме 9X_unity3d-5 использования HashSet? Или какой-то способ 9X_unity5 создать неупорядоченный кортеж?
This question похож на 9X_unity то, что я пытаюсь сделать, за исключением 9X_unity3d C#, а не python.
Ответ #1
Ответ на вопрос: Словарь С#, который использует неупорядоченную пару в качестве ключа?
Если тип не ваш или вы не можете или не хотите его изменять, обратитесь к Theodor Zoulias's answer
В противном случае, предполагая, что UnorderedPair
является 9X_unity-game-engine вашим собственным классом, вы можете изменить 9X_csharp то, что вы можете сделать, например.
[Serializable]
public class UnorderedPair : IEquatable>
{
public T X;
public T Y;
public UnorderedPair()
{
}
public UnorderedPair(T x, T y)
{
X = x;
Y = y;
}
public bool Equals(UnorderedPair other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
// For equality simply include the swapped check
return X.Equals(other.X) && Y.Equals(other.Y) || X.Equals(other.Y) && Y.Equals(other.X);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((UnorderedPair)obj);
}
public override int GetHashCode()
{
// and for the HashCode (used as key in HashSet and Dictionary) simply order them by size an hash them again ^^
var hashX = X == null ? 0 : X.GetHashCode();
var hashY = Y == null ? 0 : Y.GetHashCode();
return HashCode.Combine(Math.Min(hashX,hashY), Math.Max(hashX,hashY));
}
public static bool operator ==(UnorderedPair left, UnorderedPair right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair left, UnorderedPair right)
{
return !Equals(left, right);
}
}
а затем, например,
var testDict = new Dictionary, string>();
testDict.Add(new UnorderedPair(1,2), "Hello World!");
Console.WriteLine(testDict[new UnorderedPair(2,1)]);
Согласно 9X_unity-game-engine предложению Джодрелла в комментариях, вы 9X_dictionary даже можете сделать типы взаимозаменяемыми 9X_c#-language - не уверен, что это когда-либо понадобится 9X_unordered - но таким образом вы даже можете иметь 9X_unity3d пару разных типов:
[Serializable]
public class UnorderedPair : IEquatable>
{
public TX X;
public TY Y;
public UnorderedPair()
{
}
public UnorderedPair(TX x, TY y)
{
X = x;
Y = y;
}
public UnorderedPair(TY y, TX x)
{
X = x;
Y = y;
}
public override int GetHashCode()
{
// and for the HashCode (used as key in HashSet and Dictionary) simply order them by size an hash them again ^^
var hashX = X == null ? 0 : X.GetHashCode();
var hashY = Y == null ? 0 : Y.GetHashCode();
var combine = HashCode.Combine(Math.Min(hashX, hashY), Math.Max(hashX, hashY));
return combine;
}
public bool Equals(UnorderedPair other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
if (typeof(TX) != typeof(TY))
{
return EqualityComparer.Default.Equals(X, other.X) && EqualityComparer.Default.Equals(Y, other.Y);
}
return EqualityComparer.Default.Equals(X, other.X) && EqualityComparer.Default.Equals(Y, other.Y)
|| X.Equals(other.Y) && Y.Equals(other.X);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj switch
{
UnorderedPair other => Equals(other),
UnorderedPair otherSwapped => Equals(otherSwapped),
_ => false
};
}
public static bool operator ==(UnorderedPair left, UnorderedPair right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair left, UnorderedPair right)
{
return !Equals(left, right);
}
public static implicit operator UnorderedPair(UnorderedPair pair)
{
return new UnorderedPair(pair.Y, pair.X);
}
}
и
var testDict = new Dictionary, string>();
testDict.Add(new UnorderedPair(1,2.5), "Hello World!");
Console.WriteLine(testDict[new UnorderedPair(2.5,1)]);
Ответ #2
Ответ на вопрос: Словарь С#, который использует неупорядоченную пару в качестве ключа?
Вы можете написать собственную реализацию 9X_unity5 IEqualityComparer
и передать ее в качестве аргумента конструктору 9X_unity-game-engine вашего Dictionary
. Таким образом, вам не придется 9X_c#-language изменять тип UnorderedPair
, переопределяя его методы 9X_unity-game-engine Equals
и GetHashCode
. Ниже приведен пример такого компаратора 9X_dictionary для структуры ValueTuple
, где T1
и T2
имеют один и тот 9X_dictionary же тип:
class UnorderedValueTupleEqualityComparer : IEqualityComparer<(T, T)>
{
private readonly IEqualityComparer _comparer;
public UnorderedValueTupleEqualityComparer(IEqualityComparer comparer = default)
{
_comparer = comparer ?? EqualityComparer.Default;
}
public bool Equals((T, T) x, (T, T) y)
{
if (_comparer.Equals(x.Item1, y.Item1)
&& _comparer.Equals(x.Item2, y.Item2)) return true;
if (_comparer.Equals(x.Item1, y.Item2)
&& _comparer.Equals(x.Item2, y.Item1)) return true;
return false;
}
public int GetHashCode((T, T) obj)
{
int h1 = _comparer.GetHashCode(obj.Item1);
int h2 = _comparer.GetHashCode(obj.Item2);
if (h1 > h2) (h1, h2) = (h2, h1);
return HashCode.Combine(h1, h2);
}
}
Пример использования:
Dictionary<(int, int), string> dictionary = new(
new UnorderedValueTupleEqualityComparer());
Ответ #3
Ответ на вопрос: Словарь С#, который использует неупорядоченную пару в качестве ключа?
Вдохновленный @derHugo's answer и моими комментариями к нему,
Общая 9X_unity5 реализация,
#nullable enable
public class UnorderedPair : IEquatable>
{
private static IEqualityComparer comparer = EqualityComparer.Default;
public T X { get; }
public T Y { get; }
public UnorderedPair(T x, T y)
{
X = x;
Y = y;
}
public bool Equals(UnorderedPair? other)
{
if(other is null)
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
// For equality simply include the swapped check
return
comparer.Equals(X, other.X) && comparer.Equals(Y, other.Y)
||
comparer.Equals(X, other.Y) && comparer.Equals(Y, other.X);
}
public override bool Equals(object? obj)
{
return Equals(obj as UnorderedPair);
}
public override int GetHashCode()
{
unchecked
{
return
(X is null ? 0 : comparer.GetHashCode(X))
+
(Y is null ? 0 : comparer.GetHashCode(Y));
}
}
public static bool operator ==(UnorderedPair? left, UnorderedPair? right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair? left, UnorderedPair? right)
{
return !Equals(left, right);
}
}
#nullable disable
-
3
-
3
-
7
-
2
-
2
-
5
-
3
-
3
-
3
-
2
-
8
-
2
-
5
-
2
-
4
-
3
-
2
-
2
-
3
-
1
-
6
-
2
-
2
-
2
-
5
-
3
-
4
-
2
-
3
-
3
-
7
-
5
-
3
-
5
-
3
-
2
-
1
-
2
-
5
-
2
-
2
-
4
-
2
-
2
-
3
-
2
-
2
-
3
-
3
-
3