Рефакторинг и удаление дублирования из этого кода памятки - PullRequest
2 голосов
/ 06 января 2011

Я пытаюсь удалить некоторое дублирование из этого кода, чтобы он легко поддерживал функции с большим количеством параметров.

Как бы вы улучшили этот код и добавили более сложные функции?

Кроме того, меня беспокоит генерация ключей: некоторые объекты не будут четко сериализованы в строку, а просто вернут свое имя типа, а не уникальное значение.Предложения?

Редактировать: я использовал ответ ChaosPandion, и получил его до этого

using System;
using System.Web.Caching;

public static class Memoize
{
    public static Cache LocalCache = System.Web.HttpRuntime.Cache ?? System.Web.HttpContext.Current.Cache;

    public static TResult ResultOf<TArg1, TResult>(Func<TArg1, TResult> func, long durationInSeconds, TArg1 arg1)
    {
        var key = HashArguments(func.Method.Name, arg1);
        return ResultOf(key, durationInSeconds, () => func(arg1));
    }

    public static TResult ResultOf<TArg1, TArg2, TResult>(Func<TArg1, TArg2, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2)
    {
        var key = HashArguments(func.Method.Name, arg1, arg2);
        return ResultOf(key, durationInSeconds, () => func(arg1, arg2));
    }

    public static TResult ResultOf<TArg1, TArg2, TArg3, TResult>(Func<TArg1, TArg2, TArg3, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2, TArg3 arg3)
    {
        var key = HashArguments(func.Method.Name, arg1, arg2, arg3);
        return ResultOf(key, durationInSeconds, () => func(arg1, arg2, arg3));
    }

    public static TResult ResultOf<TArg1, TArg2, TArg3, TArg4, TResult>(Func<TArg1, TArg2, TArg3, TArg4, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
    {
        var key = HashArguments(func.Method.Name, arg1, arg2, arg3, arg4);
        return ResultOf(key, durationInSeconds, () => func(arg1, arg2, arg3, arg4));
    }

    private static TResult ResultOf<TResult>(string key, long durationInSeconds, Func<TResult> func)
    {
        return LocalCache.Get(key) != null
                   ? (TResult)LocalCache.Get(key)
                   : CacheResult(key, durationInSeconds, func());
    }

    public static void Reset()
    {
        var enumerator = LocalCache.GetEnumerator();
        while (enumerator.MoveNext())
            LocalCache.Remove(enumerator.Key.ToString());
    }

    private static T CacheResult<T>(string key, long durationInSeconds, T value)
    {
        LocalCache.Insert(key, value, null, DateTime.Now.AddSeconds(durationInSeconds), new TimeSpan());
        return value;
    }

    private static string HashArguments(params object[] args)
    {
        if (args == null)
            return "noargs";

        var result = 23;
        for (var i = 0; i < args.Length; i++)
        {
            var arg = args[i];
            if (arg == null)
            {
                result *= (31 * i + 1);
                continue;
            }
            result *= (31 * arg.GetHashCode());
        }
        return result.ToString();
    }
}

Ответы [ 2 ]

0 голосов
/ 07 января 2011

Основная идея в коде ChaosPandion верна, но есть некоторые фатальные недостатки, которые необходимо исправить, чтобы это работало надежно.

1) Вместо того, чтобы использовать хэш хэшей, вам нужно создать действительно уникальный ключ. Одним из способов является объединение строковых представлений каждого элемента в массиве параметров с разделителем. Таким образом, после того, как результат запомнен, он будет постоянно извлекаться без ложных срабатываний.

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

По общему признанию, объединение большой строки не особенно элегантно и может даже повлиять на производительность. Более того, это все еще оставляет вас с проблемой классов, ToString которых не предоставляет информацию для конкретного экземпляра. Тем не менее, есть решения для этого на основе отражения.

2) Очень простой оптимизацией было бы вызвать LocalCache.Get только один раз в Complete. Это потенциально дорогостоящая операция, поэтому на самом деле нет оправдания удвоению стоимости.

Кроме того, следуйте советам ChaosPandion.

0 голосов
/ 06 января 2011

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

EDIT

Моя последняя версия выдает довольнонадежное распространение.Взгляните на пример, который я поместил в метод Main.

class Program
{

    public static class Memoize
    {
        public static Cache LocalCache = 
            System.Web.HttpRuntime.Cache ?? System.Web.HttpContext.Current.Cache;

        public static TResult ResultOf<TArg1, TResult>(
            Func<TArg1, TResult> func, 
            TArg1 arg1, 
            long durationInSeconds)
        {
            var key = HashArguments(
                func.Method.DeclaringType.GUID,
                typeof(TArg1).GUID, 
                (object)arg1);
            return Complete(key, durationInSeconds, () => func(arg1));
        }

        public static TResult ResultOf<TArg1, TArg2, TResult>(
            Func<TArg1, TArg2, TResult> func, 
            TArg1 arg1, 
            TArg2 arg2, 
            long durationInSeconds)
        {
            var key = HashArguments(
                func.Method.DeclaringType.GUID,
                typeof(TArg1).GUID, 
                (object)arg1, 
                typeof(TArg2).GUID, 
                (object)arg2);
            return Complete(key, durationInSeconds, () => func(arg1, arg2));
        }

        public static TResult ResultOf<TArg1, TArg2, TArg3, TResult>(
            Func<TArg1, TArg2, TArg3, TResult> func, 
            TArg1 arg1, 
            TArg2 arg2, 
            TArg3 arg3, 
            long durationInSeconds)
        {
            var key = HashArguments(
                func.Method.DeclaringType.GUID,
                typeof(TArg1).GUID, 
                (object)arg1, 
                typeof(TArg2).GUID, 
                (object)arg2, 
                typeof(TArg3).GUID, 
                (object)arg3);
            return Complete(key, durationInSeconds, () => func(arg1, arg2, arg3));
        }

        public static void Reset()
        {
            var enumerator = LocalCache.GetEnumerator();
            while (enumerator.MoveNext())
                LocalCache.Remove(enumerator.Key.ToString());
        }

        private static T CacheResult<T>(string key, long durationInSeconds, T value)
        {
            LocalCache.Insert(
                key, 
                value, 
                null, 
                DateTime.Now.AddSeconds(durationInSeconds), 
                new TimeSpan());
            return value;
        }

        static T Complete<T>(string key, long durationInSeconds, Func<T> valueFunc)
        {
            return LocalCache.Get(key) != null
               ? (T)LocalCache.Get(key)
               : CacheResult(key, durationInSeconds, valueFunc());
        }

        static string HashArguments(params object[] args)
        {
            if (args == null)
                return "null args";

            int result = 23;
            for (int i = 0; i < args.Length; i++)
            {
                var arg = args[i];
                if (arg == null)
                {
                    result = 31 * result + (i + 1);
                    continue;
                }
                result = 31 * result + arg.GetHashCode();
            }
            return result.ToString();
        }
    }

    static int test(int a, int b)
    {
        return a + b;
    }

    private static class Inner
    {
        public static int test(int a, int b)
        {
            return a + b;
        }
    }

    static int test(int a, object b)
    {
        return a + (int)b;
    }

    static void Main(string[] args)
    {
        Memoize.ResultOf<int, int, int>(test, 1, 2, 100000);
        Memoize.ResultOf<int, int, int>(test, 2, 1, 100000);
        Memoize.ResultOf<int, int, int>(Inner.test, 1, 2, 100000);
        Memoize.ResultOf<int, int, int>(Inner.test, 2, 1, 100000);
        Memoize.ResultOf<int, object, int>(test, 1, 2, 100000);
        Memoize.ResultOf<int, object, int>(test, 2, 1, 100000);
    }
}
...