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