字典中结构键的值相等和 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。 :)
我正在实现一个通用字典。我希望 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。 :)