2011-01-06 67 views
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(); 
    } 
} 
+0

退房http://stackoverflow.com/questions/2852161/c-memoization-of-functions-with-arbitrary-number-of-arguments – 2011-01-06 19:24:37

+0

@Alexiei Levenkov,我读到了,但它仍然好像有是必须重复以允许更多参数的一段代码 – CaffGeek 2011-01-06 19:46:39

回答

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); 
    } 
} 
0

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

1)与其使用散列哈希值,您需要制作一个真正独特的密钥。一种方法是将参数数组中每个元素的字符串表示与分隔符连接起来。这样,一旦结果被记录下来,它就会一直被检索出来,而没有误报。

混沌与方法的GUID的技巧在这里很有用,但这可以避免取决于GetHashCode是真正独特的问题,以及依赖一个相当简单的哈希散列保持唯一。相反,完整的字符串将被散列,但会有一个完整的逐字符比较来排除不匹配。

不可否认,连接大字符串并不是特别优雅,甚至可能会影响性能。而且,这仍然给你带来ToString没有提供实例特定信息的类的问题。但是,有基于反思的解决方案。

2)一个非常简单的优化就是在Complete中拨打LocalCache.Get一次。这是一项潜在的昂贵操作,因此没有理由让成本翻番。

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