C#, сгенерируйте постоянный ключ для массива объекта

Я хотел бы сохранить в Cache объект с постоянным ключом.

    public TItem Set<TItem>(string key, TItem value)
    {
        return = _memoryCache.Set(key, value);
    }

Я получаю свой объект с помощью функции:

    public TItem Get(
        TPrimaryKey id,
        params object[] args,
        bool byPassCaching = false
    )
    {
        if (byPassCaching || !_cacheManager.TryGetValue<TItem>(GetKey(id, args), out TItem result)){
           item = [FUNCTION TO GET ITEM];
           _cacheManager.Set(GetKey(id, args), item)
        }
        return item;
    }

Я хотел бы сгенерировать постоянный ключ для TItem, Id (int) и некоторых параметров (object [] args).

Вот моя функция для генерации постоянного ключа:

    internal static string GetKey<TItem>(int id, params object[] args)
    {
        return string.Format("{0}#{1}[{2}]", typeof(TItem).FullName, id, args?[FUNCTION TO CALL]);
    }

[FUNCTION TO CALL] - это функция, которую я ищу.

Поэтому в следующий раз, когда я вызову функцию с тем же параметром, у меня будет тот же ключ.

Например

GetKey<MyClass>(1, null) => "MyApp.MyClass#1[]"
GetKey<MyClass>(1, "Hello", "World") => "MyApp.MyClass#1[sdas5d1as5d4sd8]"
GetKey<MyClass>(1, "Hello", "World") => "MyApp.MyClass#1[sdas5d1as5d4sd8]"
GetKey<MyClass>(1, item => item.Include(s => s.Categories)) => "MyApp.MyClass#1[asdasdasd87wqw]"

В начале я думал использовать GetHashCode, но генерируемый int всегда отличается.

Как я могу это сделать?

1 ответ

Я думаю, GetHashCode - это то, что вам нужно; Вы вызывали GetHashCode для массива или вызывали его для каждого члена массива? Вы хотите сделать последнее.

Следующее должно быть похоже на то, что вы хотите; если params равен нулю или пуст, или полон пустых объектов, то код будет 00000000.

В противном случае, если параметры совпадают, результирующий хеш будет таким же. И если параметры имеют другой порядок или другие значения, то хеш будет другим.

internal static string GetKey<TEntity>(int id, params object[] args)
{
    return string.Format("{0}#{1}[{2}]", typeof(TEntity).FullName, id, ArrayHash(args));
}

static string ArrayHash(params object[] values)
{
    return BitConverter.ToString(BitConverter.GetBytes(ArrayHashCode(values))).Replace("-", "").ToLowerInvariant();
}

static int ArrayHashCode(params object[] values)
{
    if (values == null || values.Length == 0) return 0;
    var value = values[0];
    int hashCode = value == null ? 0 : value.GetHashCode();
    for (int i = 1; i < values.Length; i++)
    {
        value = values[i];
        unchecked
        {
            hashCode = (hashCode * 397) ^ (value == null ? 0 : value.GetHashCode());
        }
    }
    return hashCode;
}

Обратите внимание, что 397 - это простое число, которое я выбрал из реализации GetShashCode в ReSharper, что позволяет красиво распределять хэш-коды через переполнение (изящно разрешенное для блока без контроля).

Пример хешированных параметров будет выглядеть так:

ArrayHash( 1, null, "test" )     => "dee9e1ea"
ArrayHash( 1, null, "test" )     => "dee9e1ea"
ArrayHash( null, 1, "test" )     => "fa8fe3ea"
ArrayHash("one", "two", "three") => "8841b2be"
ArrayHash()                      => "00000000"
ArrayHash(null)                  => "00000000"
ArrayHash(new object[] {})       => "00000000"

Обратите внимание, что если массив params содержит объекты, они также должны иметь правильную реализацию GetHashCode.

Другие вопросы по тегам