Я не думаю, что есть какой-то особенно эффективный способ сделать то, что вы хотите. Потребуется какая-то дополнительная обработка, чтобы убедиться, что вы получаете соответствующие коды ha sh. Кроме того, имейте в виду, что если классы, которыми вы не управляете, уже реализуют Equals и GetHashCode, а Equals возвращает true, например, которые отличаются только чем-то вроде обнуляемого логического значения, равного false или null, то для GetHashCode неверно возвращать разные значения.
Вы можете сериализовать до JSON, чтобы достичь того, что вы хотите. Это исключит любые поля, которые могут быть аннотированы для исключения. Предполагая, что ни одно из полей, относящихся к коду ha sh, не исключено, это сработает. В качестве альтернативы вы можете написать функции расширения для типов, которые будут вызывать конфликты, и настроить хеширование для этих полей. Затем используйте рефлексию (которая, вероятно, также будет использоваться для сериализации в JSON), чтобы перебрать членов класса и получить коды sh, используя ваши расширения, где это необходимо. Нечто похожее на приведенный ниже код.
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;
}
}
Вам, очевидно, придется добавить другие проверки, чтобы использовать расширение bool, добавить другие расширения и т. Д., Чтобы покрыть случаи, которые вам нужны. И сделайте тестирование, чтобы проверить влияние использования отражения для сериализации. Вы можете уменьшить это для классов, которые уже реализуют GetHashCode, например, не генерируя ha sh кодов для каждого члена для них.
И этот код, очевидно, можно очистить. Здесь просто быстро и грязно.