为何向Memoize传入方法时需显式指定Func参数的类型实参?
为什么编译器无法推断Memoize方法的类型实参?
这个问题其实是C#类型推断机制的一个常见限制——编译器没办法直接把方法组(比如你的DoIt)匹配到泛型委托参数Func<T,V>上,从而自动推断出T和V。
原因很简单:方法组本身可以被转换成多个不同的委托类型(哪怕你的方法没有重载,编译器也不会默认假设它对应某一个特定的Func<,>版本),所以编译器无法确定你要将DoIt转换成哪种Func<T,V>,只能报错让你显式指定类型。
可行的规避方案
方案1:用Lambda表达式包裹方法调用
这是最简洁的方案,通过lambda给编译器提供明确的参数和返回值类型上下文:
var cached1 = Memoize(a => DoIt(a));
Lambda的参数a会被推断为string,返回值是int,编译器就能自动确定T=string、V=int,完美解决推断问题,而且写法比显式指定类型参数清爽很多。
方案2:显式将方法组转换为委托类型
如果不想用lambda,也可以直接把方法组转换成对应的Func<T,V>,再传入Memoize:
var cached1 = Memoize((Func<string, int>)DoIt); // 或者写成 var cached1 = Memoize(new Func<string, int>(DoIt));
这种方式和你原来的Memoize<string, int>(DoIt)本质类似,但在多参数场景下,比显式指定一堆类型参数稍微直观一点。
方案3:扩展Memoize支持多参数场景(解决你提到的多类型参数痛点)
如果需要缓存多参数方法,你可以重载Memoize方法来支持不同参数数量的Func,比如针对双参数的情况:
public static Func<T1, T2, V> Memoize<T1, T2, V>(Func<T1, T2, V> f) { var cache = new ConcurrentDictionary<(T1, T2), V>(); return (a, b) => cache.GetOrAdd((a, b), _ => f(a, b)); }
调用时同样用lambda来辅助推断:
// 示例多参数方法 public static int DoItWithTwoParams(string a, int b) => a.Length + b; // 缓存调用 var cachedMulti = Memoize((a, b) => DoItWithTwoParams(a, b));
如果参数更多,你可以继续重载Func<T1,T2,T3,V>等版本,或者用C#的元组来统一处理任意参数(不过元组作为字典键需要注意值类型的相等性)。
方案4:将Memoize改为扩展方法
把Memoize改成Func<T,V>的扩展方法,语法会更自然:
public static class MemoizeExtensions { public static Func<T, V> Memoize<T, V>(this Func<T, V> f) { var cache = new ConcurrentDictionary<T, V>(); return a => cache.GetOrAdd(a, f); } }
调用时可以这样写:
var cached1 = ((Func<string, int>)DoIt).Memoize(); // 或者结合lambda var cached1 = new Func<string, int>(a => DoIt(a)).Memoize();
内容的提问来源于stack exchange,提问作者Alexei Sosin
相关产品推荐
相关产品推荐

