Link to home
Start Free TrialLog in
Avatar of pipelineconsulting
pipelineconsulting

asked on

Problem with Generic Interface Inheritance

I have the following interfaces and concrete classes which attempt to define a system for sending messages (email, fax, sms etc.):

 
public interface IMessageSender<T> where T : IMessage
    {
        bool ExecuteSend(T messageToSend);
    }

public interface IMailSender<T> : IMessageSender<T> where T : IMailMessage
    {
    }

 public class MailSender<T> : IMailSender<T> where T : IMailMessage
    {
        public bool ExecuteSend(T messageToSend)
        {
            throw new NotImplementedException();
        }
    }

Open in new window


Effectively, this defines a general IMessageSender interface which IMailSender (or ISmsSender, IFaxSender etc.) can use.  The interfaces send "message objects" which ultimately inherit from IMessage - in this case IMailMessage inherits from IMessage.

I want the IMailSender interface to narrow the type of IMessage that is acceptable to the interface - this is the only real purpose of this "intermediate" interface.  The concrete MailSender then just implements the interface.  For info, the ExecuteSend just contains the specific logic for send the particular type of message, so in this example it would execute some variety of Smtp send logic.

The IMessage, IMailMessage interfaces and concrete MailMessage class are defined as:

public interface IMessage
    {
        void Send<T>(T messageSender) where T : IMessageSender<IMessage>;

    }

public interface IMailMessage : IMessage {
  void Send(IMailSender<IMailMessage> messageSender);
}

    public class MailMessageWrapper : IMailMessage {

}

public void Send(IMailSender<IMailMessage> messageSender) {
     // automatic pre-send logic here.
     messageSender.ExecuteSend(this);
}

Open in new window


The problem comes when using IMessage.  I want to be able to make the message responsible for sending itself - i.e. inject a IMessageSender into the message rather than inject an IMessage into an IMessageSender. this is because it may be necessary to execute some automatic operation on the message before sending.

My concrete class is throwing an error that the interface member IMessage.Send<T>(T) is not implemented.

I need to find a way around this that will still allow me to restrict the IMessageSender to the appropriate type - i.e. in the concrete class I want to write (or some slight variation on):

public void Send(IMailSender<IMailMessage> messageSender)

NOT

void Send<T>(T messageSender) where T : IMessageSender<IMessage>
Avatar of Todd Gerbert
Todd Gerbert
Flag of United States of America image

It's early in the morning, I haven't had my coffee yet, and your question is a little tricky for me to wrap my half-conscious brain around, so forgive me if I'm way off base...

Are generics really necessary here?  That is, give:
public interface IMessageSender<T> where T : IMessage
{
        bool ExecuteSend(T messageToSend);
}

Open in new window



Doesn't this code block achieve precisely the same thing, considering that your limiting T to IMessage so you know whatever type is passed in to T will always be able to be cast to an IMessage, so:
public interface IMessageSender
{
    bool ExecuteSend(IMessage messageToSend);
}

Open in new window


Avatar of pipelineconsulting
pipelineconsulting

ASKER

The reason I did this (and my head is a bit fuzzy now) is to have the ability to restrict the sender to accept a certain type of message further down the inheritance line.

So, for an ISmsSender, I only want it to deal with ISmsMessages rather than say an IMailMessage (which obviously both inherit from IMessage)

For the concrete classes, this would end up something like

 
public class MailSender<IMailMessage> {
  bool ExecuteSend(IMailMessage messageToSend) {}
}

public class SmsSender<ISmsMessage> {
  bool ExecuteSend(ISmsMessage messageToSend) {}
}

Open in new window


but not

 
public class MailSender<ISmsMessage> {
  bool ExecuteSend(ISmsMessage messageToSend) {}
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Todd Gerbert
Todd Gerbert
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks - that should get things moving!
Hope that actually helped - that was a bit of a brain-teaser question, took me a couple days to wrap my head around it and I'm not quite sure I succeeded. ;)