为 MemoryCache(或任何缓存)实现通用 Get<T>

Implement Generic Get<T> for MemoryCache (or any Cache)

我正在尝试编写 "simple" 通用 Get<T>;扩展为 System.Runtime.MemoryCache.

为什么 "simple"?因为通常我在缓存之前就知道对象的真实类型,所以当我从缓存中检索它时,我不会以不可预测的方式转换它。

例如:如果 boolean "true" 值以 cacheKey "id" 存储在缓存中,那么

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

这是我不完整的实现:

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

非常感谢任何建议。

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

更新(2016 年 4 月 11 日):

对于给出的那些好的建议,我实现了我的第一个版本的 Get

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 ... */
}

提案:

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

用法(假设你在缓存中有一个int类型的id):

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

我没有测试过。

基本工作是编写 CastValue 以将任何对象转换为所需类型。而且它不必处理非常复杂的情况,因为缓存中的对象类型对于程序员来说是可预测的。这是我的版本。

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