2

我试图从这段代码中删除一些重复,并让它轻松支持具有更多参数的函数。

您将如何改进此代码并允许更复杂的功能?

另外,我担心我的密钥生成,一些对象不会明显地序列化为字符串,而只是返回它们的类型名,而不是唯一值。建议?

编辑:我使用了 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();
    }
}
4

2 回答 2

0

这是我的版本,它消除了相当多的重复,并且应该产生更可靠的密钥范围。

编辑

我的最新版本产生了一个非常可靠的发行版。看一下我在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);
    }
}
于 2011-01-06T20:38:38.170 回答
0

ChaosPandion 代码中的基本思想是正确的,但是必须修复一些致命缺陷才能使其可靠地工作。

1)您需要创建一个真正唯一的密钥,而不是使用哈希哈希。一种方法是将参数数组中每个元素的字符串表示形式与分隔符连接起来。这样,一旦结果被记忆,它将被一致地检索,而不会出现误报。

Chaos 使用方法的 GUID 的技巧在这里很有用,但这避免了依赖GetHashCode真正唯一性以及依靠相当简单的哈希值来保持唯一性的问题。相反,完整的字符串将被散列,但将进行完整的逐个字符比较以排除不匹配。

诚然,连接一个大字符串并不是特别优雅,甚至可能会影响性能。此外,这仍然会给您留下 ToString 不提供特定于实例信息的类的问题。但是,有一些基于反射的解决方案。

2) 一个非常简单的优化是LocalCache.GetComplete. 这是一项潜在的昂贵操作,因此确实没有任何借口可以将成本翻倍。

除此之外,按照 ChaosPandion 的建议去做。

于 2011-01-07T18:39:25.530 回答