Приведение анонимного типа из типа Object

Я пытаюсь использовать класс System.Runtime.Caching.MemoryCache в.NET 4.0. У меня есть метод, который является общим, поэтому я могу передать любой тип в кэш-память и получить его обратно при вызове.

Метод возвращает объект типа object, который является анонимным типом с полем Value, которое содержит кэшированный объект.

У меня вопрос, как я могу привести объект, который я возвращаю, в соответствующий ему тип?

Ниже мой код...

public static class ObjectCache
{
    private static MemoryCache _cache = new MemoryCache("GetAllMakes");

    public static object GetItem(string key)
    {
        return AddOrGetExisting(key, () => InitialiseItem(key));
    }

    private static T AddOrGetExisting<T>(string key, Func<T> valueFactory)
    {
        var newValue = new Lazy<T>(valueFactory);
        var oldValue = _cache.AddOrGetExisting(key, newValue, new CacheItemPolicy()) as Lazy<T>;

        try
        {
            return (oldValue ?? newValue).Value;
        }
        catch
        {
            _cache.Remove(key);
            throw;
        }
    }

    /// <summary>
    /// How can i access Value and cast to type "List<IBrowseStockVehicle>"
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    private static object InitialiseItem(string key)
    {
        // SearchVehicleData.GetAllMakes(false) is of type List<IBrowseStockVehicle>
        return new { Value = SearchVehicleData.GetAllMakes(false) };
    }
}

и юнит тест...

    [TestMethod]
    public void TestGetAllMakes_Cached()
    {
        dynamic ReturnObj = ObjectCache.GetItem("GetAllMakes");

        // *********************************************
        // cannot do this as tester is of type Object and doesnt have teh field Value
        foreach(IBrowseStockVehicle item in ReturnObj.Value)
        {

        }
    }

3 ответа

Ты не можешь Анонимные типы... анонимны. У них нет имени типа, которое вы можете использовать, поэтому используйте тип вместо этого.

Конечно, вы все еще можете использовать Reflection, но в этом случае это может быть не очень полезно:

var x = ReturnObj.GetType().GetProperty("Value").GetValue(ReturnObj);

У меня вопрос, как я могу привести объект, который я возвращаю, в соответствующий ему тип?

Вы не можете сделать это! Анонимные типы являются анонимными с высокоуровневой / семантической точки зрения (т.е. вы не можете привести к неизвестному типу, не так ли?), И они являются внутренними и со случайным именем с низкоуровневой точки зрения. То есть они недоступны.

Я могу предложить вам два подхода:

Динамические объекты на помощь

В своем вопросе вы сказали, что не можете получить доступ к собственности object, но вы можете реализовать простой DynamicObject для динамического доступа к любому свойству объекта:

public sealed class DynamicWrapper : DynamicObject
{
    public DynamicWrapper(object target)
    {
        Target = target;

        // We store property names and property metadata in a dictionary
        // to speed up things later (we'll find if a requested
        // property exists with a time complexity O(1)!)
        TargetProperties = target.GetType()
                                    .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                    .ToDictionary(p => p.Name, p => p);

    }

    private IDictionary<string, PropertyInfo> TargetProperties { get; }
    private object Target { get; }


    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        // We don't support setting properties!
        throw new NotSupportedException();
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        PropertyInfo property;

        if(TargetProperties.TryGetValue(binder.Name, out property))
        {
            result = property.GetValue(Target); 

            return true;
        }
        else

        {
            result = null;

            return false;
        }
    }
}

И используйте всю обертку следующим образом:

var obj = new { Text = "hello world" };

dynamic dynObj = new DynamicWrapper(obj);
string text = dynObj.Text;

Заключение

  • Сохраните и извлеките ваш кэшированный объект, завернутый в нечто вроде DynamicWrapper и это будет работать, как вы ожидаете!

  • В противном случае используйте словари.

  • Или, как уже говорили другие авторы, не используйте анонимный тип и не храните конкретные типы.

Вам лучше использовать дженерики повсюду, а не только для AddOrGetExisting<T>,

Кроме того, лучше не делать Cache ответственным за создание новых объектов. Это должен быть служебный класс, он должен придерживаться принципа единой ответственности и не должен иметь ссылок на ваш бизнес или уровни данных.


В качестве примера я добавлю класс, который я использую для MVC. Не использует MemoryCacheвместо этого он использует HttpRuntime.Cache так что это может быть не тот ответ, который вам нужен, но он может помочь вам найти лучшее решение в отношении использования непатентованных средств и принципа единой ответственности.

namespace Xyz.WebLibrary
{
    public static class Cache
    {
        // Get the value from the HttpRuntime.Cache that was stored using the cacheKey (if any). Returns true if a matching object of requested type T was found in the cache. Otherwise false is returned, along with a default(T) object or value.
        public static bool Get<T>(string cacheKey, out T result)
        {
            if (!string.IsNullOrEmpty(cacheKey))
            {
                object o = HttpRuntime.Cache.Get(cacheKey);
                if (o != null && o is T)
                {
                    result = (T)o;
                    return true;
                }
            }
            result = default(T);
            return false;
        }

        // Store a value in the HttpRuntime.Cache using the cacheKey and the specified expiration time in minutes.
        public static void Set(string cacheKey, object o, int slidingMinutes)
        {
            if (!string.IsNullOrEmpty(cacheKey) && slidingMinutes > 0)
                HttpRuntime.Cache.Insert(cacheKey, o, null, DateTime.MaxValue, TimeSpan.FromMinutes(slidingMinutes), CacheItemPriority.Normal, null);
        }

        // Erase the value from the HttpRuntime.Cache that was stored using the cacheKey (if any).
        public static void Erase(string cacheKey)
        {
            if (!string.IsNullOrEmpty(cacheKey) && HttpRuntime.Cache.Get(cacheKey) != null)
                HttpRuntime.Cache.Remove(cacheKey);
        }
    }
}

Использование:

ProductInfo p;
int id = 12345;
string key = "ProductInfo_" + id;
if (!Cache.Get(key, out p))
{
    p = GetProductInfoFromDB(id);
    Cache.Set(key, p, slidingMinutes: 5);
}
Другие вопросы по тегам