2016-04-07 31 views
8

Sto cercando di scrivere un "semplice" Generico Get <T> estensione per System.Runtime.MemoryCacheImplementare Generico Get <T> per MemoryCache (o qualsiasi Cache)

Perché "semplice"? Perché generalmente conosco il tipo reale dell'oggetto prima di memorizzarlo nella cache, quindi quando lo recupero dalla cache, non lo convertirò in modi imprevedibili.

Per esempio: se booleano valore "vero" è memorizzato nella cache con cacheKey "id", in modo da

Get<string>("id") == "true"; 
Get<int>("id") == 1; // any result > 0 is okay 
Get<SomeUnpredictableType> == null; // just ignore these trouble conversions 

Ecco la mia L'implementazione 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); 
} 

Tutti i suggerimenti sono molto apprezzati.

===================================

Aggiornamento (2016/04/11):

Per quei bei suggerimenti dati, implemento la mia prima versione di 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 ... */ 
} 
+1

È possibile utilizzare (T) Convert.ChangeType (valore, typeof (T)) ma questa sarebbe solo una parte del proprio metodo generico. –

+0

Grazie, ho aggiunto al mio codice di esempio. Non sono ancora sicuro se ci sono alcune condizioni che ho perso nella sezione TODO. – ineztia

+0

Scrivi test unitari, aggiungi tutti i casi che desideri gestire. –

risposta

1

L'essenziale è scrivere un CastValue <T> per convertire qualsiasi oggetto nel tipo desiderato. E non deve gestire condizioni molto complicate perché i tipi di oggetto nella cache sono prevedibili per il programmatore. Ed ecco la mia versione.

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); 
} 
2

Proposta:

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; 
} 

Usage (supponiamo d si dispone di un ID di tipo int nella cache):

int id = Get<int>("id"); 
int? mayBeId = Get<int?>("id"); 
string idAsString = Get<int?>("id")?.ToString(); 
double idAsDouble = (double)Get<int>("id"); 

non ho la prova di esso.

+0

Sembra che manchino alcune condizioni, poiché si presuppone che possa sempre restituire un valore (T). Ho testato e ottenuto l'eccezione "Cast specificato non valido". – ineztia

+0

Cosa hai aggiunto alla cache, cosa hai provato a leggere da esso? –

+0

numeri, testi o qualsiasi oggetto caricato pesantemente (entità) @StefanSteinegger – ineztia