I am writing a simple Memoize
helper that allows caching method results instead of computing them every time. However, when I try to pass a method into Memoize
, the compiler can't determine the type arguments. Aren't they obvious from my method signature? Is there a way around this?
Sample code:
using System;
using System.Collections.Concurrent;
public static class Program
{
public static Func<T, V> Memoize<T, V>(Func<T, V> f)
{
var cache = new ConcurrentDictionary<T, V>();
return a => cache.GetOrAdd(a, f);
}
// This is the method I wish to memoize
public static int DoIt(string a) => a.Length;
static void Main()
{
// This line fails to compile (see later for error message)
var cached1 = Memoize(DoIt);
// This works, but is ugly (and doesn't scale to lots of type parameters)
var cached2 = Memoize<string, int>(DoIt);
}
}
Error message:
error CS0411: The type arguments for method 'Program.Memoize<T, V>(Func<T, V>)'
cannot be inferred from the usage. Try specifying the type arguments explicitly.
Isn't
DoIt()
signature compatible withFunc<string, int>
?
Yes it is. It's fine to convert it to that specific type, like this for example:
Func<string, int> func = DoIt;
var cachedDoit = Memoize(func);
The problem you're running into is that type inference basically doesn't work particularly well with method group conversions. When you pass DoIt
as an argument, that's a method group. In your case it only refers to a single method, but it could refer to multiple methods, with different signatures... and that complicates things.
I often see this come up with LINQ, where I'd like to call foo.Select(SomeMethodGroup)
, but type inference fails. There's some support for method groups within type inference, but it's not everything we might want it to be.
This isn't a matter of the C# team being lazy... type inference is hugely complicated, and any change is really fraught with danger in terms of backward compatibility. It's in section 7.5.2 of the C# 5 specification if you want to have a look - but frankly that's part of the spec where I get lost really quickly.
See more on this question at Stackoverflow