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:
Is there a technically (or philosophically) superior preference between these two implementations?
Are these different at runtime?
If they are both left in, which one is being called in the getter method?
1 Answer 1
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.
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 withdynamic
will work in most cases.HttpRuntime.Cache
expects and returnsobject
types meaning all types are being downcast or boxed. Therefore, if your function returns aUser
object, what is stored is still anobject
and what is returned from the cache is downcast likewise. Therefore youruser.Username
property will not be available until you cast, even though it's dynamic.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 callingToString()
orGetHashCode()
, 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.Obviously I'm going to say always use the generic version in this case.
-
\$\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– evilertoaster2014年02月05日 22:57:04 +00:00Commented Feb 5, 2014 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– George Mauer2014年02月05日 23:10:14 +00:00Commented Feb 5, 2014 at 23:10
-
\$\begingroup\$ He also mis-PascalCased 'a lot' in his fake generic method name. \$\endgroup\$Magus– Magus2014年02月06日 16:20:47 +00:00Commented Feb 6, 2014 at 16:20
-
1\$\begingroup\$ I think you misunderstand how
dynamic
work. If I understand you correctly, something likeobject 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 toobject
". \$\endgroup\$svick– svick2014年02月18日 15:07:22 +00:00Commented Feb 18, 2014 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– George Mauer2014年02月18日 15:13:33 +00:00Commented Feb 18, 2014 at 15:13