Приведение анонимного типа из типа 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)
        {

        }
    }

person JGilmartin    schedule 04.01.2017    source источник
comment
Ответ: не используйте для этого анонимный тип, используйте правильный именованный тип.   -  person Lasse V. Karlsen    schedule 04.01.2017
comment
Решением является объявление типа.   -  person Ant P    schedule 04.01.2017
comment
Анонимные типы доступны как internal, что означает, что вы должны добавить атрибут сборки, чтобы предоставить внутренние типы в тестовый проект с помощью атрибута InternalsVisibleToAttribute.   -  person Igor    schedule 04.01.2017
comment
Внутри одной сборки могут быть .... хаки, но кросс-сборка никуда не годится. Используйте правильный именованный тип.   -  person Lasse V. Karlsen    schedule 04.01.2017


Ответы (3)


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

Конечно, вы все еще можете использовать Reflection, но в данном случае это может оказаться непригодным для использования:

var x = ReturnObj.GetType().GetProperty("Value").GetValue(ReturnObj);
person Patrick Hofman    schedule 04.01.2017

Лучше использовать дженерики повсюду, а не только для 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);
}
person Peter B    schedule 04.01.2017

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

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

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

На помощь приходят динамические объекты

В своем вопросе вы сказали, что не можете получить доступ к свойству 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, и он будет работать так, как вы ожидаете!

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

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

person Matías Fidemraizer    schedule 04.01.2017