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