I'm trying to write a "simple" Generic Get<T>
; extension for
System.Runtime.MemoryCache
.
Why "simple" ? Because generally I know object's real type before caching it, so when I retrieve it from cache, I'm not going to convert it in unpredictable ways.
For example: if boolean "true" value is stored in cache with cacheKey "id", so
Get<string>("id") == "true";
Get<int>("id") == 1; // any result > 0 is okay
Get<SomeUnpredictableType> == null; // just ignore these trouble conversions
Here's my incomplete implemention:
public static T DoGet<T>(this MemoryCache cache, string key) {
object value = cache.Get(key);
if (value == null) {
return default(T);
}
if (value is T) {
return (T)value;
}
// TODO: (I'm not sure if following logic is okay or not)
// 1. if T and value are both numeric type (e.g. long => double), how to code it?
// 2. if T is string, call something like Convert.ToString()
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
Any suggestions are highly appreciated.
===================================
Update (04/11/2016):
For those nice suggestions given, I implement my first version of Get<T>
public class MemCache {
private class LazyObject<T> : Lazy<T> {
public LazyObject(Func<T> valueFactory) : base(valueFactory) { }
public LazyObject(Func<T> valueFactory, LazyThreadSafetyMode mode) : base(valueFactory, mode) { }
}
private static T CastValue<T>(object value) {
if (value == null || value is DBNull) {
return default(T);
}
Type valType = value.GetType();
if (valType.IsGenericType && valType.GetGenericTypeDefinition() == typeof(LazyObject<>)) {
return CastValue<T>(valType.GetProperty("Value").GetValue(value));
}
if (value is T) {
return (T)value;
}
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(t) && typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
private MemoryCache m_cache;
public T Get<T>(string key) {
return CastValue<T>(m_cache.Get(key));
}
public void Set<T>(string key, T value, CacheDependency dependency) {
m_cache.Set(key, value, dependency.AsCacheItemPolicy());
}
public T GetOrAdd<T>(string key, Func<T> fnValueFactory, CacheDependency dependency) {
LazyObject<T> noo = new LazyObject<T>(fnValueFactory, LazyThreadSafetyMode.ExecutionAndPublication);
LazyObject<T> old = m_cache.AddOrGetExisting(key, noo, dependency.AsCacheItemPolicy()) as LazyObject<T>;
try {
return CastValue<T>((old ?? noo).Value);
} catch {
m_cache.Remove(key);
throw;
}
}
/* Remove/Trim ... */
}
The essential work is to write a CastValue<T> to convert any object to desired type. And it doesn't have to handle very complicate condition because object types in cache is predictable for the programmer. And here's my version.
public static T CastValue<T>(object value) {
if (value == null || value is DBNull) {
return default(T);
}
if (value is T) {
return (T)value;
}
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(t) && typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
Proposal:
public static T DoGet<T>(this MemoryCache cache, string key)
{
object value = cache.Get(key);
if (value == null) {
return default(T);
}
// support for nullables. Do not waste performance with
// type conversions if it is not a nullable.
var underlyingType = Nullable.GetUnderlyingType(t);
if (underlyingType != null)
{
value = Convert.ChangeType(value, underlyingType);
}
return (T)value;
}
Usage (supposed you have an id of type int
in the cache):
int id = Get<int>("id");
int? mayBeId = Get<int?>("id");
string idAsString = Get<int?>("id")?.ToString();
double idAsDouble = (double)Get<int>("id");
I haven't test it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With