字典中结构键的值相等和 class 键的引用相等

Value equality for struct keys and reference equality for class keys in a dictionary

我正在实现一个通用字典。我希望 TKey 是结构或 class。如果它是一个结构,我想按值比较键,否则按引用。

我既不能使用 Object.Equals(仅适用于结构)也不能使用 Object.ReferenceEquals(仅适用于引用类型)。那我用什么方法来判断是否相等呢?

== 运算符可能会解决这个问题,但如果不为键 (where TKey : ...) 指定任何约束,我就无法使用它。我应该声明哪些接口?

您可以在 class 中执行以下操作:

public class MyCustomDictionary<TKey, TValue>
{
    private static readonly Func<TKey, TKey, bool> _equalityComparer;

    // ... other stuff

    static MyCustomDictionary()
    {
        if (typeof(TKey).IsClass)
            _equalityComparer = (lhs, rhs) => Object.ReferenceEquals(lhs, rhs)
        else
            _equalityComparer = (lhs, rhs) => lhs.Equals(rhs);
    }

    // ... other stuff

}

并使用这个相等比较器进行比较。

虽然通常这样做的方式是使用相等比较器 IEqualityComparer<TKey>,如下所示:

public class MyCustomDictionary<TKey, TValue>
{
    private readonly IEqualityComparer<TKey> _equalityComparer;

    // ... other stuff

    public MyCustomDictionary()
    {
        _equalityComparer = EqualityComparer<T>.Default;
    }

    public MyCustomDictionary(IEqualityComparer<T> comparer)
    {
        _equalityComparer = comparer;
    }

    // ... other stuff

}

这是完成的,例如在常规 BCL System.Collections.Generic.Dictionary<TKey, TValue> 和其他需要进行相等比较的集合中。

如果您没有非常特殊的需求(就像我最初在阅读您的问题时所想的那样),您应该使用标准方法 IEqualityComparer<TKey>.

.NET 通用字典使用 Equals 和 GetHashCode 方法,它们是虚拟的并且可用于结构和 class。所以你可以简单地做同样的事情,只是覆盖你结构中的那些方法:

public struct KeyStructure 
{
    public override bool Equals(object obj)
    {
        // your implementation
    }

    public override int GetHashCode()
    {
        // your implementation
    }
}

另一方面,一般来说,如果您想使用接口进行类型限制,您只需创建一个具有相同 Equals 和 GetHashCode 方法的接口,并将其添加到您想要支持的任何类型。

public interface IKey
{
    bool Equals(object value);
    int GetHashCode();
}

public struct KeyStruct : IKey
{
}

public class KeyClass :IKey
{
}

public class MyDictionary<TKey, TValue> where TKey : IKey
{
}

I can't use neither Object.Equals (only works with structs) nor Object.ReferenceEquals (only works with reference types).

看来您可能只是误解了它们的工作原理。无论是处理值类型还是引用类型,System.Object.Equals() 方法实现都同样有效(没有双关语意)。

对于值类型,它会逐个字段进行比较。如果被比较的两个值是相同的类型,并且它们的每个字段具有相同的值,那么它们被认为是相等的。

对于引用类型,它只是使用引用相等性,正如您所希望的那样。

请注意,类型可以覆盖此方法,因此实际使用的实现可能与上述不同。例如,string 类型会覆盖该方法,因此两个不是同一实例的字符串仍然可以比较为相等。但默认情况下,会发生上述情况。

最后我会注意到,如果您想要的是一种 Dictionary<TKey, TValue> class 完全一样的行为,很可能是最好的解决方案是只使用 class。 :)