tags:

views:

584

answers:

4

I'm talking about c# 3.5 3.0. I know how to do it when cache or ServiceProvider can have only one instance for the whole application. In this case ServiceProvider can look like this

public static class Service<T>
{
    public static T Value {get; set;}
}

and can be used for different types like this:

Service<IDbConnection>.Value = new SqlConnection("...");
Service<IDesigner>.Value = ...;
//...
IDbCommand cmd = Service<IDbConnection>.Value.CreateCommand();

Static cache is also easy:

public static class Cache<T>
{
    private static Dictionary<int, T> cache = new Dictionary<int, T>();

    public static void Add(int key, T value)
    {
        cache.Add(key, value);
    }

    public static T Find(int key)
    {
        return cache[key];
    }
}

and can be used like this:

Cache<string>.Add(1, "test");
Cache<DateTime>.Add(2, DateTime.Now);
//...
string found = Cache<string>.Find(1);


My question is: how can I create similiar cache or service provider when I want to have 2 or more different instances of each. Here is example code, how I want to use service provider:

ServiceProvider provider = new ServiceProvider();
provider.Add<IDbConnection>(new SqlConnection("..."));
provider.Add<IDesigner>(...);
//...
ServiceProvider provider1 = new ServiceProvider();
provider1.Add<IDbConnection>(new SqlConnection("..."));
//...
//...
IDbCommand cmd1 = provider.GetService<IDbConnection>().CreateCommand();
IDbCommand cmd2 = provider1.GetService<IDbConnection>().CreateCommand();

The only implementation that I have in my head is using casting which I want to avoid.

public class ServiceProvider
{
    private Dictionary<Type, object> services = new Dictionary<Type, object>();
    public void Add<T>(T value)
    {
        services.Add(typeof(T), value);
    }

    public T GetService<T>()
    {
        return (T) services[typeof (T)];
    }
}
+2  A: 

Why are you particularly desperate to avoid casting? Yes, it feels "unsafe" - but you can basically guarantee that it's not going to be an issue in ServiceProvider, and the clients aren't doing any casting.

This is a reasonably common problem, but I don't believe there's any nice solution to it within .NET generics - it's a type relationship which can't be expressed, basically.

EDIT: I've now blogged about this and encapsulated the behaviour in a type. Feel free to take that code if it keeps things cleaner for you.

Jon Skeet
Thinking about it, this is a really common issue. I think it's worth implementing a type which encapsulates exactly this part (the dictionary add/lookup) so that the casting is really tightly restricted to that single class. Will experiment tonight and write a blog entry...
Jon Skeet
From tests that I found on the internet it seems that generic version of code is usually 4-5 times faster than the one with cast. I think it's a good reason to investigate this and cache is a good example of code that will benefit from this.
SeeR
How much of the time do you expect to be pulling stuff out of the cache? Do you really think it will be a performance bottleneck? If you're doing *anything* even slightly networky, I'd expect any difference due to a cast to disappear into the noise.
Jon Skeet
One guy in our company wrote application that downloads something like 100 000 records from database at start to perform some calculations befpre showing anything to user. This is good example when cache class will benefit from this. But my question is: is it possible? not is it usable :-)
SeeR
How long do you think 100,000 casts will take, really? I did a quick benchmark on my Eee PC (i.e. hardly server class hardware!) and it was able to perform 100 million casts in about 1.5 seconds. But back to theory, no, I don't believe you can get away without the cast.
Jon Skeet
+1  A: 

As I posted to Jon Skeet's blog, the following approach might help you avoid casts, if that's a worry (though perhaps this introduces some other more serious issues than casting :)).

If you have a weak dictionary implementation (one that uses weak-reference keys and cleans out otherwise unreferenced keys and their associated values), you could try something like this:

   public class TypeDictionary

   {

       private class InnerTypeDictionary<T>

       {

           static WeakDictionary<TypeDictionary, T> _innerDictionary = new WeakDictionary<TypeDictionary, T>();

           public static void Add(TypeDictionary dic, T value)

           {

               _innerDictionary.Add(dic, value);

           }

           public static T GetValue(TypeDictionary dic)

           {

               return _innerDictionary[dic];

           }

       }

       public void Add<T>(T value)

       {

           InnerTypeDictionary<T>.Add(this, value);

       }

       public T GetValue<T>()

       {

           return InnerTypeDictionary<T>.GetValue(this);

       }

   }

It has the benefit of making all the type lookups into static generic type lookups, without direct recourse to System.Type objects, so I guess that might give you a performance kick. Would be interested to know if it does suit your caching scenario.

James Hart
A: 
SeeR
I don't see why the methods in Service<T> are instance methods at all. I'd be very wary of the GC implications of this - Service<T> itself really relies on everything being removed, or it'll hang around forever. That may be suitable for your application, but I'd be happy with casting personally.
Jon Skeet
They are instance because ServiceProvider Need to know what types to remove.According to safety Making Service<T>.services public or protected and using GC.Collect() One can easily unit test if this solution behaves correctly and don't have memory leaks.
SeeR
Oh, and I don't have any concrete application in mind - it was rather academic curiosity if it's possible to write application without cast. Maybe I'll use it in some project in the future :-)
SeeR
A: 

There is not a good way to do this without casting. Don't get hung up on the casting cost. Focus on things that actually impact performance... for example, hashing isn't free to begin with. You shouldn't be calling back into your service provider every time you want to use a service. Get the reference a single time and you don't have to worry about mounting costs of retrieval:

var service = provider.GetService();

service.DoSomething();

service.DoSomethingElse();

jezell