Вы хотите иметь возможность беспрепятственного обмена реализациями, но вы не можете просто принять любую реализацию, поэтому я кое-что сделал в прошлом и сработал довольно хорошо:
Создайте две версии интерфейса, чтобы иметь возможность беспрепятственного обмена без указания типов:
interface IMessagingExchange
{
void Bind(IMessagingQueue queue);
}
interface IMessagingExchange<TQueue> : IMessagingExchange where TQueue : IMessagingQueue
{
void Bind(TQueue queue);
}
Создание интерфейса для определения подмножества очередей, которые можно использовать с конкретным обменом. В этом случае мы определяем очереди, которые могут использоваться Rabbit Exchange:
interface IRabbitQueue : IMessagingQueue { }
Класс RabbitQueue
теперь реализует этот интерфейс:
class RabbitQueue : IRabbitQueue { }
Мы добавили базовый класс для проверки типов:
abstract class ExchangeBase<TQueue> : IMessagingExchange<TQueue> where TQueue : class, IMessagingQueue
{
public abstract void Bind(TQueue queue);
public void Bind(IMessagingQueue queue)
{
var typedQueue = queue as TQueue;
if (typedQueue == null)
throw new InvalidOperationException($"This exchange only supports queues that implement {typeof(TQueue).FullName}");
Bind(typedQueue);
}
}
Обратите внимание, что базовый класс реализует нетипизированную версию, выполняет проверку типов для вас и перенаправляет запрос на типизированную версию, которая предоставляется конкретным классом.
Класс RabbitExchange
теперь наследуется от базового класса и обеспечивает логику привязки:
class RabbitExchange : ExchangeBase<IRabbitQueue>
{
public override void Bind(IRabbitQueue queue)
{
}
}
Исполнение:
//Using untyped versions
IMessagingExchange exchange = new RabbitExchange();
IMessagingQueue queue = new RabbitQueue();
//This works fine
exchange.Bind(queue);
//Attempt to use the wrong queue
IMessagingQueue memoryQueue = new InMemoryQueue();
//This results in an error
exchange.Bind(memoryQueue);
Этот обмен поддерживает только те очереди, которые реализуют
SomeNameSpace.IRabbitQueue
//We use a typed exchange this time
var rabbitExchange = exchange as RabbitExchange;
//This works
rabbitExchange.Bind(queue);
//This is still allowed because of the untyped interface, but causes an error because type is still checked
rabbitExchange.Bind(memoryQueue);
//Use a typed queue this time
var rabbitQueue = queue as RabbitQueue;
//This skips the base class validation because it calls the typed method in the concrete class
rabbitExchange.Bind(rabbitQueue);