c# - Implementar Generic Get<T> para MemoryCache(o cualquier caché)
generics caching (2)
Intento escribir una extensión Generic Get <T> "simple" para System.Runtime.MemoryCache
¿Por qué "simple"? Porque generalmente conozco el tipo real del objeto antes de almacenarlo en la memoria caché, de modo que cuando lo recupero de la memoria caché, no voy a convertirlo de manera impredecible.
Por ejemplo: si el valor booleano "verdadero" se almacena en caché con cacheKey "id", entonces
Get<string>("id") == "true";
Get<int>("id") == 1; // any result > 0 is okay
Get<SomeUnpredictableType> == null; // just ignore these trouble conversions
Aquí está mi implementación incompleta:
pubic 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);
}
Cualquier sugerencia es altamente apreciada.
================================
Actualización (04/11/2016):
Para esas agradables sugerencias dadas, implemento mi primera versión de 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 ... */
}
El trabajo esencial es escribir CastValue <T> para convertir cualquier objeto al tipo deseado. Y no tiene que manejar condiciones muy complicadas porque los tipos de objetos en la memoria caché son predecibles para el programador. Y esta es mi versión.
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);
}
Propuesta:
pubic 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;
}
Uso (se supone que tiene una identificación de tipo int
en la memoria caché):
int id = Get<int>("id");
int? mayBeId = Get<int?>("id");
string idAsString = Get<int?>("id")?.ToString();
double idAsDouble = (double)Get<int>("id");
No lo probé.