For last few months I'm more and more fascinated with .NET generics. Especially interesting for me is a (totally academic) question: Is it possible to write application without using an cast?
While considering that option first 2 problematic cases I found was some kind of cache or ServiceProvider implementations. I even asked a question on stackoverflow.com if it's possible, but the only answer I had was that it's not or that I should have something like WeakDictionary - which probably will use cast in its implementation.
But I wouldn't be oneself if I will abandon this subject :-). As I showed in question it's possible for only one instance using static generic fields, but I can't find the solution for non static objects. After few days I think I found the solution to this - not so elegant, but it should work.
So here you have fully generic (but simplified) implementation of ServiceProvider:
6 public class ServiceContainer : IDisposable
7 {
8 readonly IList<IService> services = new List<IService>();
9
10 public void Add<T>(T service)
11 {
12 Add<T,T>(service);
13 }
14
15 public void Add<Key, T>(T service) where T : Key
16 {
17 services.Add(new Service<Key>(this, service));
18 }
19
20 public void Dispose()
21 {
22 foreach(var service in services)
23 service.Remove(this);
24 }
25
26 ~ServiceContainer()
27 {
28 Dispose();
29 }
30
31 public T Get<T>()
32 {
33 return Service<T>.Get(this);
34 }
35 }
36
37 public interface IService
38 {
39 void Remove(object parent);
40 }
41
42 public class Service<T> : IService
43 {
44 static readonly Dictionary<object, T> services = new Dictionary<object, T>();
45
46 public Service(object parent, T service)
47 {
48 services.Add(parent, service);
49 }
50
51 public void Remove(object parent)
52 {
53 services.Remove(parent);
54 }
55
56 public static T Get(object parent)
57 {
58 return services[parent];
59 }
60 }
As you can see I store all services in static generic Dictionary, which also have information to which serviceProvider this service belong. Normally when ServiceProvider will be cleaned by Garbage Collector, all services also will be removed from memory. But because I have static field that holds them, to avoid memory leaks I'm cleaning those services in destructor/finalizer o ServiceProvider.
I see two drawbacks of this solution
- Larger memory usage due to need of having two lists of references to those services
- ServiceProvider stays one generation longer in memory than normal solution with cast, because of finalizer usage.
Now with this example Implementation of cache without cast shouldn't be so hard :-)
LATE EDIT (2009-07-30): As someone may notice from my answer on stackoverflow.com I tried to implement such ServiceProvider but failed to do it without memory leaks. Uhh :-(
My request to microsoft: Please implement WeakReference<T>.