实现给定对象能够返回哈希键的功能的最佳方法是什么?
要求为:
HashCodeFn(((bool?)false, "example")) != HashCodeFn(((bool?)null, "example"))
[Serializable]
属性)我已经尝试过.GetHashCode
,但是:
null
vs0
vs false
之类的东西不可靠我尝试过:
private static int GetHashKey<T>(T input)
{
using var memoryStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, input);
memoryStream.Position = 0;
using var reader = new StreamReader(memoryStream);
return reader.ReadToEnd().GetHashCode();
}
但是:
[Serializable]
(某些类型我无法控制,并且不实现它们)我正在考虑以最紧凑的形式将对象序列化为JSON,然后获取该字符串的GetHashCode
,但是我不确定它与NodaTime.Instant
之类的东西配合得如何。这是最快的方法吗?
如果用作理解用例,这将用作数据加载器密钥(有关示例,请参见github.com/graphql/dataloader)。
特别是使用数据加载器密钥来处理批处理。例如,当您有许多要求使用输入(a, b, c)
的请求并且想要“旋转” a
时(这意味着(1, b, c), (2, b, c), (3, b, c)
应该调用批处理函数fn([1, 2, 3], (b, c))
,那么您需要能够定义键对于将(b, c)
的相同值用作数据加载器键,这是相同的。
例如从输入的角度来看,在b
之类的东西上指定是否布尔值被认为是2种不同的东西,应该在两个不同的函数上进行批处理。
如果我要使用(b, c).GetHashCode()
,那么我会认为((bool?)false, "ok")
和((bool?)null, "ok")
是相同的,因此将它们批处理到相同的批处理函数中会产生意外的结果。
问题是实现给定对象能够返回哈希键的功能的最佳方法是什么?要求为:HashCodeFn((((bool?)false,“ example”)))!= HashCodeFn(((bool?)null,“ ...
我认为没有任何特别有效的方法可以完成您想要的事情。将需要某种额外的处理,以确保您获得适当的哈希码。另外,请记住,如果您无法控制的类已经实现了Equals且GetHashCode和Equals返回true,例如,它们之间的区别仅在于诸如nullable布尔值false或null之类的差异,则GetHashCode返回不同的值是不正确的。] >
您可以序列化为JSON以实现所需的功能。这将排除任何可能被注释为排除的字段。假设没有任何与哈希码相关的字段被排除,那么它将起作用。或者,您可以为将导致冲突的类型编写扩展功能,并为这些字段自定义哈希。然后使用反射(也可能在序列化为JSON中使用反射)遍历类成员,并在必要时使用扩展使用哈希码。类似于下面的代码。
class ThingToHash
{
public bool? CouldBeFalseOrNullOrNull { get; }
public int IncludesZero { get; }
public string CanBeEmptyOrNull { get; }
private string Hidden { get; }
public ThingToHash(bool? couldBeFalseOrNull, int includesZero, string canBeEmptyOrNull)
{
CouldBeFalseOrNullOrNull = couldBeFalseOrNull;
IncludesZero = includesZero;
CanBeEmptyOrNull = canBeEmptyOrNull;
}
}
static class StringExtensions
{
public static int GetAltHashCode(this string toHash)
{
return toHash?.GetHashCode() ?? 17;
}
}
static class NullableBoolExtensions
{
public static int GetAltHashCode(this bool? toHash)
{
return toHash?.GetAltHashCode() ?? true.GetHashCode() * 19;
}
}
static class BoolExtensions
{
public static int GetAltHashCode(this bool toHash)
{
if (false == toHash)
{
return true.GetHashCode() * 17;
}
return toHash.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(false.GetHashCode());
Console.WriteLine(((bool?)null).GetHashCode());
Console.WriteLine(false == (bool?)null);
Console.WriteLine(HashUnknownObject(new ThingToHash(null, 0, "")));
Console.WriteLine(HashUnknownObject(new ThingToHash(false, 0, "")));
Console.ReadKey();
}
static int HashUnknownObject(Object toHash)
{
PropertyInfo[] members = toHash.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
int hash = 17;
foreach (PropertyInfo memberToHash in members)
{
object memberVal = memberToHash.GetValue(toHash);
if (null == memberVal)
{
if (typeof(bool?) == memberToHash.PropertyType)
{
hash += 31 * ((bool?)null).GetAltHashCode();
}
else if (typeof(string) == memberToHash.PropertyType)
{
hash += 31 * ((string)null).GetAltHashCode();
}
}
else
{
hash += 31 * memberToHash.GetValue(toHash).GetHashCode();
}
}
return hash;
}
}