While working with a colleague on a project I got into a debate about event-handling versus delegates stuffed into a service container. It sounded weird to me at the time; I've always been a huge fan of event-driven programming so I tend to lean that way when I need to make my objects responsive to one another. The way my colleague looks at it, "everything's a service, so if you just add your methods that you need to run to your service container you can call them when you need them." The idea seemed truly nutty to me so I just had to code it up. It is important to note here that our system already made use of the ServiceContainer approach. We have a custom service container that our applications use; in this way we can add any service to the container at run-time and provide our whole application (and all of it's components) with access to everything via the service container. If you're not familliar with that approach this may seem a little off-kilter.
First thing I did was to create a custom ServiceContainer implementation. The code for my GenericServiceContainer is below. Note the extra method I've added which makes use of generic types.
7 public class GenericServiceContainer : System.ComponentModel.Design.ServiceContainer
8 {
9 public virtual T GetService<T>() where T : class
10 {
11 return this.GetService(typeof(T)) as T;
12 }
13 }
Next, I'll create a few delegate types. In this way, any class that has knowledge of these delegate types can request the GSC send'em out.
11 public delegate void MessageDelegate(string message);
12
13 public delegate int AdditionDelegate(int x, int y);
Though the functionality abstracted in the form of delegates I still need to create a class that can "do the work" for the application. To accomplish this I've written a simple WorkerClass, the code for which is below.
15 public class WorkerClass
16 {
17 public static void ShowMessage(string message)
18 {
19 Console.WriteLine(message);
20 }
21
22 public static int Add(int x, int y)
23 {
24 return (x + y);
25 }
26 }
Finally, some tests will prove the theory.
28 [TestFixture]
29 public class TestServiceContainer
30 {
31 GenericServiceContainer _container;
32
33 [SetUp]
34 public void Setup()
35 {
36 _container = new GenericServiceContainer();
37
38 _container.AddService(typeof(MessageDelegate),
39 new MessageDelegate(WorkerClass.ShowMessage));
40 _container.AddService(typeof(AdditionDelegate),
41 new AdditionDelegate(WorkerClass.Add));
42 }
43
44 [TearDown]
45 public void TearDown()
46 {
47 _container.Dispose();
48 }
49
50 [Test]
51 public void TestMessageDelegate()
52 {
53 MessageDelegate del = _container.GetService<MessageDelegate>();
54 Assert.IsNotNull(del);
55
56 del.Invoke("Testing");
57 }
58
59 [Test]
60 public void TestAdditionDelegate()
61 {
62 AdditionDelegate del = _container.GetService<AdditionDelegate>();
63 Assert.IsNotNull(del);
64
65 int result = del.Invoke(2, 2);
66 Assert.AreEqual(result, (2 + 2));
67 }
68 }
So that's it! With this code I've defined the structure of the methods that will be doing the work and allowed redirection to a worker class to perform the work. Now, any class within the application being augmented by my custom ServiceContainer can have easy access to centralized functionality.
Happy Coding!