Search code examples
c#.netgenericsmemorycache

Casting anonymous type from type Object


I’m trying to utilise the System.Runtime.Caching.MemoryCache class in .NET 4.0. I have a method that is generic so I can pass any type into the memory cache and get it back when invoked.

The method returns an object of type object which is an anonymous type with a field Value which contains the cached object.

My question is, how can I cast the object I’m getting back into its corresponding type?

Below is my code…

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

and the 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)
        {

        }
    }

Solution

  • My question is, how can I cast the object I’m getting back into its corresponding type?

    You can't do this! Anonymous types are anonymous from a high-level/semantic point of view (i.e. you can't cast to an unknown type, can you?), and they're internal and with a random name from a low level point of view. That is, they're inaccessible.

    I can suggest you two approaches:

    Dynamic objects to the rescue

    In your question you said that you can't access a property of object, but you can implement a simple DynamicObject to access any object property dynamically:

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

    And use the whole wrapper as follows:

    var obj = new { Text = "hello world" };
    
    dynamic dynObj = new DynamicWrapper(obj);
    string text = dynObj.Text;
    

    Conclusion

    • Store and retrieve your cached object wrapped with something like DynamicWrapper and it will work as you expect!

    • Otherwise use dictionaries.

    • Or, like other answerers have already said, don't use anonymous type and store concrete types.