I called it MagicCache at first, thinking I’d come up with something new and cool! But, as usual, I’d actually just forgotten the name.
Memoization is a useful but intrusive technique. It’s the kind of thing that I want to be able to wield around my code and put it where I need it, almost as a configuration-time parameter. So I’m not quite satisfied with just writing the code myself each time I need it. This is a perfect case for AOP.
The approach I took was not to use an existing AOP framework, since I think that would freak my coworkers out a little, but rather to do my own simple system. It uses the fantastic DynamicProxy (as do all C# AOP systems I know of) to inject code, and I use a custom attribute to mark the invocations I care about. The result is quite pleasant to work with:
virtual int Sum(int a, int b)
Or, in more complicated cases:
virtual object BuildObject(string paramA, int paramB)
I like this particular method because the caching policy is nicely separated from the actual logic, but has enough proximity to see what’s going on. And I don’t have to write the caching code again and again, which means I’m free to enhance the general caching algorithm.
Of course it’s not quite as easy as all that. Memoized methods must be virtual, for DynamicProxy to be able to intercept the invocation, and the object in question must be wrapped in a proxy object. In my case I wrapped it all up in a single method call,
Memoizer.CreateProxy(obj). For objects that are already built in a factory this is not problem at all, but sometimes it’s kind of inconvenient.
I’ve also added the ability to treat returned
strings as paths to files which are deleted when they are removed from the cache, though I haven’t thought through it well enough to be generally useful.