Я пытаюсь разработать общий командный процессор. Я хотел бы создать классы обработчиков команд, реализующие данный интерфейс. Я буду использовать инверсию управления для динамического создания экземпляра соответствующего класса в зависимости от типа полученной команды. Затем я хотел бы вызвать метод «Выполнить» класса в общем виде.
Я могу сделать эту работу, используя параметр ковариантного типа, но в этом случае я не могу использовать параметр универсального типа в качестве параметра метода.
Казалось бы, контравариантный подход должен работать, потому что он позволяет мне объявлять параметры метода по желанию, но, к сожалению, экземпляр класса не может быть преобразован в базовый интерфейс.
Код ниже иллюстрирует проблему:
using System;
using System.Diagnostics;
namespace ConsoleApplication2
{
// Command classes
public class CommandMessage
{
public DateTime IssuedAt { get; set; }
}
public class CreateOrderMessage : CommandMessage
{
public string CustomerName { get; set; }
}
// Covariant solution
public interface ICommandMessageHandler1<out T> where T : CommandMessage
{
void Execute(CommandMessage command);
}
public class CreateOrderHandler1 : ICommandMessageHandler1<CreateOrderMessage>
{
public void Execute(CommandMessage command)
{
// An explicit typecast is required
var createOrderMessage = (CreateOrderMessage) command;
Debug.WriteLine("CustomerName: " + createOrderMessage.CustomerName);
}
}
// Contravariant attempt (doesn't work)
public interface ICommandMessageHandler2<in T> where T : CommandMessage
{
void Execute(T command);
}
public class CreateOrderHandler2 : ICommandMessageHandler2<CreateOrderMessage>
{
public void Execute(CreateOrderMessage command)
{
// Ideally, no typecast would be required
Debug.WriteLine("CustomerName: " + command.CustomerName);
}
}
class Program
{
static void Main(string[] args)
{
var message = new CreateOrderMessage {CustomerName = "ACME"};
// This code works
var handler1 = new CreateOrderHandler1();
ICommandMessageHandler1<CreateOrderMessage> handler1b = handler1;
var handler1c = (ICommandMessageHandler1<CommandMessage>) handler1;
handler1c.Execute(message);
// This code throws InvalidCastException
var handler2 = new CreateOrderHandler2();
ICommandMessageHandler2<CreateOrderMessage> handler2b = handler2;
var handler2c = (ICommandMessageHandler2<CommandMessage>)handler2; // throws InvalidCastException
handler2c.Execute(message);
}
}
}