JMorgan
JMorgan

Reputation: 805

Generics, Interfaces and Activator

I'm working on a memory caching application that can load different cache providers via configuration. I'm doing this by defining an interface and using a plugin model to load an assembly and class that parses a file and loads a class that matches the interface. I'd really like to define the interface and with a generic type that allows any object to be stored by the backing cache (whether that be a wrapper around third party cache client like Redis or NCache, or my own MemoryCache implementation ect.) with the ultimate goal of having a factory that I can point to a dll that wraps my caching methodology.

The issue that I am running into is errors about Generics when I use Activator to try to instantiate the plugin. Does anyone know a way to load a class with generics to an interface?

Basic interface

public interface ICacheProvider<T>
{
    T Get(string key);
    bool Set(string key, T value, TimeSpan expiration);
}

Factory calls to load

public static ICacheProvider<T> GetCacheProvider(string path, string className, string configuration)
{            
    ...
    Assembly asm = Assembly.LoadFile(path);
    Type type = asm.GetType(className);

    return Activator.CreateInstance(type, new object[] { configuration }) as ICacheProvider<T>;
    ...
}

Upvotes: 3

Views: 147

Answers (1)

Anton Gogolev
Anton Gogolev

Reputation: 115769

Sorry, but I don't think this is good design (and I know this is not CodeReview SE).

ICacheProvider<T> is not very usable, because there's nothing in it that would require a separate type for each cacheable object. The following is, IMO, much better, plus it relieves you of CreateInstance headache:

public interface ICacheProvider
{
    T Get<T>(string key);
    bool Set<T>(string key, T value, TimeSpan expiration);
}

Going further, I'm more than sure that concrete implementations of ICacheProvider work with plain objects, regardless of their concrete type. With this, you can further simplify your contract to:

public interface ICacheProvider
{
    object Get(string key);
    bool Set(string key, object value, TimeSpan expiration);
}

and a couple extension methods:

public static class CacheProviderExtensions
{
    public static T Get<T>(this ICacheProvider cacheProvider, string key);
    public static bool Set<T>(this ICacheProvider cacheProvider, string key, 
      T value, TimeSpan expiration);
}

Now, one of the most common operations with caches is "get-or-add" behavior, which can be expressed as yet another method on ICacheProvider:

object GetOrSet(string key, Func<object> valueProvider, TimeSpan expiration)

Upvotes: 6

Related Questions