10
\$\begingroup\$

I've implemented a common wrapper pattern I've seen for the .NET cache class using generics as follows:

private static T CacheGet<T>(Func<T> refreashFunction, [CallerMemberName]string keyName = null)
{
    if (HttpRuntime.Cache[keyName] == null)
        HttpRuntime.Cache.Insert(keyName, refreashFunction(), null, DateTime.UtcNow.AddSeconds(600), System.Web.Caching.Cache.NoSlidingExpiration);
    return (T)HttpRuntime.Cache[keyName];
}

It could then be called like so:

public static Dictionary<string, string> SomeCacheableProperty
{
    get
    {
        return CacheGet(() =>
        {
            Dictionary<string, string> returnVal = AlotOfWork();
            return returnVal;
        });
    }
}

However, the CacheGet method could be implemented using dynamic:

private static dynamic CacheGet(Func<object> refreashFunction, [CallerMemberName]string keyName = null)
{
    if (HttpRuntime.Cache[keyName] == null)
        HttpRuntime.Cache.Insert(keyName, refreashFunction(), null, DateTime.UtcNow.AddSeconds(600), System.Web.Caching.Cache.NoSlidingExpiration);
    return HttpRuntime.Cache[keyName];
}

The questions I have:

  1. Is there a technically (or philosophically) superior preference between these two implementations?

  2. Are these different at runtime?

  3. If they are both left in, which one is being called in the getter method?

\$\endgroup\$
4
\$\begingroup\$

First of all, I think you misspelled refresh as refreash.

Second, your usage can probably be simplified

CacheGet(AlotOfWork);

Finally, you might want to check if refreshFunction returns null and maybe log a warning then as that function returning null would cause a cache miss every time.

Now to answer your specific questions.

  1. I'll say it, the generic implementation is better. dynamic is a great trapdoor when you get really bogged down with generics or anonymous types and there's neat things you can do with it (see Dapper) but it still has some gotchas. For example, I do not think your function with dynamic will work in most cases.

    HttpRuntime.Cache expects and returns object types meaning all types are being downcast or boxed. Therefore, if your function returns a User object, what is stored is still an object and what is returned from the cache is downcast likewise. Therefore your user.Username property will not be available until you cast, even though it's dynamic.

  2. Yes. The generic version - with some subtle yet real differences - will run as if it was written for the type you're filling <T> with. The dynamic version will just be a "value" and let the DLR figure out how to invoke members (which again, unless you're calling ToString() or GetHashCode(), will fail). dynamic will also be slower as the runtime binding has to be done every time, though admittedly this is unlikely to be any sort of bottleneck.

  3. Obviously I'm going to say always use the generic version in this case.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the help and recommendations. The context for the question assumes that the calls to CacheGet will be wrapped by a property 'getter'. In your User example, the getter seems to cast it implicitly (properties are available via intellisense and it compiles and runes correctly). Also: Question #3 was about which version 'would' be called if both where present (as overloads), not what 'should' be called (that was question #1) \$\endgroup\$ – evilertoaster Feb 5 '14 at 22:57
  • \$\begingroup\$ @evilertoaster Ah I see, I believe the more specific generic version would take precedence but I'm not 100% sure. \$\endgroup\$ – George Mauer Feb 5 '14 at 23:10
  • \$\begingroup\$ He also mis-PascalCased 'a lot' in his fake generic method name. \$\endgroup\$ – Magus Feb 6 '14 at 16:20
  • 1
    \$\begingroup\$ I think you misunderstand how dynamic work. If I understand you correctly, something like object objectUser = new User(); dynamic dynamicUser = objectUser; Console.WriteLine(dynamicUser.UserName); shouldn't work. But it does. There is no such thing as “dynamic, but downcast to object”. \$\endgroup\$ – svick Feb 18 '14 at 15:07
  • \$\begingroup\$ @svick Hmm, you're right. I would have thought that would error but it does not. I have definitely run into issues with this in years past however, I'll try to find an example. \$\endgroup\$ – George Mauer Feb 18 '14 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.