Showing posts with label Generics. Show all posts
Showing posts with label Generics. Show all posts

Friday, 29 April 2011

Covariance and contravariance in c-sharp 4

Wow. They are some scary fucking words. The brilliant Jon Skeet does a great job of describing what they are all about in his tekpub video, but you need to pay for access ( well worth doing btw). Im going to try my best to describe what they are and how they can be useful.

Normally when we declare an interface or a class, we would do something like this:

 interface IFoo <T> {  
    T GiveMeSomething()  
    void TakeSomething(T instance)   
 }  

This can provide us with restrictions though. Say we have the following class declarations:

 class Fruit { }  
 class Apple : Fruit {}  
 class Banana : Fruit {}  

One would expect that you could do something like this:

 List<Fruit> fruitBowl = new List<Fruit>();  
 fruitBowl.Add(new Apple());  

Obviously, we can not without casting it down to it's base type. The reason for this, is that we can read and write to the list. I could request an item for the list, but I don't really know what type it would be as I could add any type of fruit. This goes against the whole point of a strongly typed List. So naturally, we get a compile error.

Covariance is all to do with values coming out of an interface/class. Effectively making it read only.

 interface IFoo <out T> {  
    T GiveMeSomething()  
 }  

Covariance is effectively saying you can not modify this object, only take from it, hence the out parameter. The famous example being IEnumerbale<T>.

Contravariance is when values only go into a class/interface. Effectively making it write only.

 interface IFoo <in T> {  
    void TakeSomething(T instance)  
 }  

This is nice as it allows us to pass in any type of fruit, without having to cast it down to the base class. So I can do the following:

 IFoo<Fruit> fruitbowl = new Foo<Fruit>();  
 fruitBowl.TakeSomething(new Apple());  
 fruitBowl.TakeSomething(new Banana());  
 fruitBowl.TakeSomething(new Apple());  

Wednesday, 27 April 2011

Domain Events

We recently trialled using domain events in one of our .net applications. Domain events are basically a simple way to delegate functionality to allow you keep you code clean.

The most common use is for you to raise an event which will send an email after completing some action, but there are many more uses.

First we have a class that will handle all ensure any events for a particular action (TEvent) are processed.

 public class EventNotifier : IEventNotifier  
   {  
     public void Notify<TEvent>(TEvent @event) where TEvent : class, IDomainEvent  
     {  
       var handlers = DependencyInjector.Instance.GetAllInstances<IHandle<TEvent>>();  
       foreach (var handler in handlers)  
       {  
         handler.Handle(@event);  
       }  
     }  
   }  

All of the events for a given action will implement a simple marker interface called IHandle and by using generics we can inject the events at runtime.

 public interface IHandle<in TEvent> where TEvent : class, IDomainEvent  
   {  
     void Handle(TEvent @event);  
   }   

An event is effectively just a model which we create and pass into our event notifier.

 public class FeedbackSubmitted : IDomainEvent  
   {  
     public Feedback Feedback { get; private set; }  
     public FeedbackSubmitted(Feedback feedback)  
     {  
       Feedback = feedback;  
     }  
   }  

The class that performs the action for a given event is pretty simple. It just needs to implement the Handle method for the given type (event - which is just a class!).


 public class OnFeedbackSubmittedSendEmail : IHandle<FeedbackSubmitted>  
   {  
     private readonly IEmailTemplateRepository m_emailTemplateRepository;  
     private readonly IFeedbackEmailSettings m_mailSettings;  
     private readonly IMailService m_mailService;  
     public OnFeedbackSubmittedSendEmail(IEmailTemplateRepository emailTemplateRepository, IMailService mailService, IFeedbackEmailSettings mailSettings)  
     {  
       m_emailTemplateRepository = emailTemplateRepository;  
       m_mailService = mailService;  
       m_mailSettings = mailSettings;  
     }  
     public void Handle(FeedbackSubmitted @event)  
     {  
       var template = m_emailTemplateRepository.GetFeedbackTemplate();  
       if (template != null)  
       {  
         var feedback = @event.Feedback;  
         var message = template.CreateMessage(new MailAddress(m_mailSettings.FeedbackEmailTo),  
                                 new Hashtable  
                                   {  
                                     {"dateSubmitted", feedback.Posted},  
                                     {"firstName", feedback.FirstName},  
                                                    {"lastName", feedback.LastName},  
                                     {"email", feedback.EmailAddress},  
                                     {"phone", feedback.PhoneNumber},  
                                                       {"comment", feedback.Comment}  
                                   }  
           );  
         m_mailService.SendMail(message);  
       }  
     }  
   }  

So once we have set all this up, we can continue to add actions for the event without modifying our core business logic. It allows us to avoid having our services becoming very cluttered.