编程语言
首页 > 编程语言> > c# – 如何使用逆变参数将通用接口转换为基类型?

c# – 如何使用逆变参数将通用接口转换为基类型?

作者:互联网

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

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

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

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

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);
        }
    }
}

解决方法:

您可以将带有通用参数的通用接口仅转换为具有更多特定参数的接口.例如. ICommandMessageHandler1&LT CommandMessage&GT可以转换为ICommandMessageHandler2< CreateOrderMessage> (Execute(CommandMessage命令)也将接受CreateOrderMessage),但反之亦然.

例如,尝试考虑是否允许在代码中抛出InvalidCastException,如果调用handler2c.Execute(new CommandMessage())会发生什么?

标签:c,covariance,contravariance,generics
来源: https://codeday.me/bug/20190704/1377309.html