Словарь С#, который использует неупорядоченную пару в качестве ключа?

Я пытаюсь создать словарь 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.

10
0
3
Общее количество ответов: 3

Ответ #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)]);

(.NET Fiddle for both)

11
0

Ответ #2

Ответ на вопрос: Словарь С#, который использует неупорядоченную пару в качестве ключа?

Вы можете написать собственную реализацию 9X_unity5 IEqualityComparer> и передать ее в качестве аргумента конструктору 9X_unity-game-engine вашего Dictionary, TValue>. Таким образом, вам не придется 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());

10
0

Ответ #3

Ответ на вопрос: Словарь С#, который использует неупорядоченную пару в качестве ключа?

Вдохновленный @derHugo's answer и моими комментариями к нему,

Fiddle here

Общая 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

6
0