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

C# 如何使用逆变参数将泛型接口强制转换为基类型?,c#,generics,covariance,contravariance,C#,Generics,Covariance,Contravariance,我正在尝试开发一个通用的命令处理器。我想创建实现给定接口的命令处理程序类。我将使用控制反转,根据收到的命令类型,以友好方式创建相应类的实例。然后我想以一种通用的方式调用类的“Execute”方法 我可以使用协变类型参数来实现这一点,但在这种情况下,我不能使用泛型类型参数作为方法参数 看起来逆变方法应该可以工作,因为它允许我根据需要声明方法参数,但不幸的是类的实例无法转换为基接口 下面的代码举例说明了该问题: using System; using System.Diagnostics; nam

我正在尝试开发一个通用的命令处理器。我想创建实现给定接口的命令处理程序类。我将使用控制反转,根据收到的命令类型,以友好方式创建相应类的实例。然后我想以一种通用的方式调用类的“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);
        }
    }
}
使用系统;
使用系统诊断;
命名空间控制台应用程序2
{
//命令类
公共类命令消息
{
在{get;set;}上发布公共日期时间
}
公共类CreateOrderMessage:CommandMessage
{
公共字符串CustomerName{get;set;}
}
//协变解
公共接口ICommandMessageHandler1,其中T:CommandMessage
{
无效执行(CommandMessage命令);
}
公共类CreateOrderHandler1:ICommandMessageHandler1
{
public void Execute(CommandMessage命令)
{
//需要显式类型转换
var createOrderMessage=(createOrderMessage)命令;
WriteLine(“CustomerName:+createOrderMessage.CustomerName”);
}
}
//逆变尝试(无效)
公共接口ICommandMessageHandler2,其中T:CommandMessage
{
无效执行(T命令);
}
public类CreateOrderHandler2:ICommandMessageHandler2
{
public void Execute(CreateOrderMessage命令)
{
//理想情况下,不需要类型转换
Debug.WriteLine(“CustomerName:+command.CustomerName”);
}
}
班级计划
{
静态void Main(字符串[]参数)
{
var message=new CreateOrderMessage{CustomerName=“ACME”};
//此代码有效
var handler1=new CreateOrderHandler1();
ICommandMessageHandler1 handler1b=handler1;
var handler1c=(icommandsessagehandler1)handler1;
handler1c.Execute(消息);
//此代码引发InvalidCastException
var handler2=new CreateOrderHandler2();
ICommandMessageHandler2 handler2b=handler2;
var handler2c=(ICommandMessageHandler2)handler2;//引发InvalidCastException
handler2c.Execute(消息);
}
}
}

您只能将带有
通用参数的通用接口强制转换为带有更多特定参数的接口。E、 g.
ICommandMessageHandler1
可以转换为
ICommandMessageHandler2
Execute(CommandMessage命令)
也将接受
CreateOrderMessage
),但反之亦然


试着想想,例如,如果允许演员在你的代码中抛出一个
InvalidCastException
,那么如果你调用
handler2c.Execute(newcommandmessage())

接口
ICommandMessageHandler1
ICommandMessageHandler2
是互不相关的。仅仅因为两者都有一个方法
Execute
,并不能使它们兼容。这将是duck类型,这在c#中是不受支持的。

也许我没有真正理解您想要做的事情,但是这对我来说没有任何类型转换就很好了

public class CommandMessage
{
    public DateTime IssuedAt
    {
        get;
        set;
    }
}

public class CreateOrderMessage : CommandMessage
{
    public string CustomerName
    {
        get;
        set;
    }
}

public interface ICommandMessageHandler2<in T> where T : CommandMessage
{
    void Execute(T command);
}
public class CreateOrderHandler2 : ICommandMessageHandler2<CreateOrderMessage>
{
    public void Execute(CreateOrderMessage command)
    {
        // No typecast is required
        Debug.WriteLine("CustomerName: " + command.CustomerName);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var message = new CreateOrderMessage
        {
            CustomerName = "ACME"
        };

        // This code throws InvalidCastException
        var handler2 = (ICommandMessageHandler2<CreateOrderMessage>)new CreateOrderHandler2();
        handler2.Execute(message);
    }
}
公共类命令消息
{
发布的公共日期时间
{
得到;
设置
}
}
公共类CreateOrderMessage:CommandMessage
{
公共字符串客户名称
{
得到;
设置
}
}
公共接口ICommandMessageHandler2,其中T:CommandMessage
{
无效执行(T命令);
}
公共类CreateOrderHandler2:ICommandMessageHandler2
{
public void Execute(CreateOrderMessage命令)
{
//不需要类型转换
Debug.WriteLine(“CustomerName:+command.CustomerName”);
}
}
班级计划
{
静态void Main(字符串[]参数)
{
var message=新建CreateOrderMessage
{
CustomerName=“ACME”
};
//此代码引发InvalidCastException
var handler2=(ICommandMessageHandler2)新建CreateOrderHandler2();
handler2.执行(消息);
}
}

我的例子是错误的。我不想在ICommandMessageHandler1和ICommandMessageHandler2之间进行任何转换;它们只是我尝试过的两种不同的选择。倒数第二行应该是:“var handler2c=(icommandsessagehandler2)handler2;”我同意。你的解释很清楚。如果我可以执行该类型转换,我将能够在CreateOrderHandler2的实例需要CreateOrderMessage时使用CommandMessage类型的参数调用Execute。这种方法似乎没有什么意义。当使用协变解决方案时,对Execute内部显式类型转换的要求似乎不再那么笨拙了。这确实有效,但仅适用于我只有一种命令的情况,因为有一个显式类型转换正在使用。我试图做的是拥有一个通用的命令处理器,它可以接收不同的命令消息,并将它们分派到相应处理程序类的实例中。我可能可以使用反射来解决这个问题,但我正在尝试使用控制反转来创建相应类的实例(我已经解决了这一部分),然后在不指定派生类的情况下调用Execute方法。我的例子中的“协变解”是有效的。