从Object类型转换为匿名类型

4
我正在尝试在.NET 4.0中使用System.Runtime.Caching.MemoryCache类。我有一个通用方法,所以我可以将任何类型传递到内存缓存中,并在调用时获取它。

该方法返回的是一个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) };
    }
}

以及单元测试(Unit Test)...

    [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)
        {

        }
    }

2
答案是:不要使用匿名类型,而应使用命名合适的类型。 - Lasse V. Karlsen
2
这个问题的解决方案是声明一个类型。 - Ant P
匿名类型可作为“internal”访问,这意味着您必须添加一个程序集属性来使用“InternalsVisibleToAttribute”属性将内部类型暴露给测试项目。 - Igor
在同一个程序集中,可能存在一些“hack”的方式,但是跨程序集则行不通。使用适当命名的类型。 - Lasse V. Karlsen
3个回答

5
您无法使用匿名类型,因为它们是匿名的。它们没有类型名称可供使用,因此请改用一个类型。 当然,您仍然可以使用反射,但在这种情况下可能并不实用:
var x = ReturnObj.GetType().GetProperty("Value").GetValue(ReturnObj);

0

最好在所有地方都使用泛型,而不仅仅是用于AddOrGetExisting<T>

此外,最好不要让缓存负责创建新对象。它应该是一个实用类,应该遵循单一职责原则,并且不应该与您的业务或数据层有关联。


作为示例,我将添加一个我用于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);
}

0
我的问题是,我如何将返回的对象转换为其对应的类型?
你做不到这一点!匿名类型从高级/语义角度来看是匿名的(即你能够将其强制转换为未知类型吗?),而从低级别的角度来看,它们是内部的,并且具有随机名称。也就是说,它们是无法访问的。
我可以向您提供两种方法:
1. 将整个对象转换为字典,并使用键访问其属性。我以前写过一些答案,对于像您这样的简单情况,这些答案应该很有用:Mapping object to dictionary and vice versaHow to convert class into Dictionary<string,string>? 2. 动态对象来拯救!
动态对象来拯救!
在你的问题中,你说你无法访问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的东西中进行存储和检索,它将按您预期的方式工作!

  • 否则,请使用字典。

  • 或者,像其他答案者已经提到的那样,不要使用匿名类型并存储具体类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接