如何使用逆变参数将泛型接口转换为基类型?

Fer*_*eia 5 c# generics covariance contravariance

我正在尝试开发通用命令处理器。我想创建实现给定接口的命令处理程序类。我将使用控制反转根据收到的命令类型动态创建适当类的实例。然后我想以通用方式调用类的“执行”方法。

我可以使用协变类型参数来完成这项工作,但在这种情况下,我不能使用泛型类型参数作为方法参数。

似乎逆变方法应该有效,因为它允许我根据需要声明方法参数,但不幸的是,类的实例无法转换为基接口。

下面的代码举例说明了这个问题:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

pen*_*tur 2

out只能将具有通用参数的通用接口转换为具有更具体参数的接口。例如ICommandMessageHandler1<CommandMessage>可以转换为ICommandMessageHandler2<CreateOrderMessage>Execute(CommandMessage command)也将接受CreateOrderMessage),但反之则不然。

例如,尝试思考一下,如果InvalidCastException允许在代码中抛出 an 的强制转换,那么如果您调用 会发生什么handler2c.Execute(new CommandMessage())