如何使用逆变参数将泛型接口转换为基类型?
我正在尝试开发一个通用命令处理器。我想创建实现给定接口的命令处理程序类。我将使用控制反转根据收到的命令类型动态创建适当类的实例。然后我想以通用方式调用该类的“执行”方法。
我可以使用协变类型参数来完成这项工作,但在这种情况下,我不能使用泛型类型参数作为方法参数。
看起来逆变方法应该可行,因为它允许我根据需要声明方法参数,但不幸的是,类的实例无法转换为基接口。
下面的代码举例说明了该问题:
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);
}
}
}
I'm trying to develop a generic command processor. I would like to create command handler classes implementing a given interface. I'll use inversion of control to dinamically create an instance of the appropriate class based on the type of command received. Then I'd like to call the "Execute" method of the class in a generic way.
I'm able to make this work using a covariant type parameter but in this case I can't use the generic type parameter as a method parameter.
It would seem that a contravariant approach should work, because it allows me to declare the method parameters as desired, but unfortunately the instance of the class can't be converted to the base interface.
The code below exemplifies the problem:
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);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您只能将具有
out
泛型参数的泛型接口强制转换为具有更多特定 参数的接口。例如,ICommandMessageHandler1
可以转换为ICommandMessageHandler2
(Execute(CommandMessage command)
也将接受CreateOrderMessage
),但反之则不然。例如,尝试思考一下,如果允许在代码中抛出
InvalidCastException
的强制转换,那么如果您调用handler2c.Execute(new CommandMessage())
会发生什么?You can cast generic interfaces with
out
generic parameters only to interfaces with more specific parameters. E.g.ICommandMessageHandler1<CommandMessage>
could be casted toICommandMessageHandler2<CreateOrderMessage>
(Execute(CommandMessage command)
will also acceptCreateOrderMessage
), but not vice versa.Try to think, for example, if the cast throwing an
InvalidCastException
in your code would be allowed, what would happened if you calledhandler2c.Execute(new CommandMessage())
?接口
ICommandMessageHandler1
和ICommandMessageHandler2
彼此不相关。仅仅因为两者都有一个方法Execute
并不能使它们兼容。这将是鸭子类型,c# 中不支持这种类型。The interfaces
ICommandMessageHandler1<T>
andICommandMessageHandler2<T>
are not related to each other. Just because both have a methodExecute
does not make them compatible. This would be duck-typing, which is not supported in c#.也许我不太明白你想要做什么,但这对我来说效果很好,没有任何类型转换。
Perhaps I don't really get what you want to do, but this works fine for me without any typecasts.