2012-02-15 79 views
4

我想开发一个通用的命令处理器。我想创建实现给定接口的命令处理程序类。我将使用控制反转来根据收到的命令类型创建适当类的实例。然后我想以通用的方式调用该类的“Execute”方法。如何使用逆变参数将通用接口转换为基本类型?

我能够使用covariant类型参数进行这项工作,但在这种情况下,我不能使用泛型类型参数作为方法参数。

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

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

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

您可以out泛型参数只投接口通用接口与更具体参数。例如。 ICommandMessageHandler1<CommandMessage>可能被铸造为ICommandMessageHandler2<CreateOrderMessage>Execute(CommandMessage command)也将接受CreateOrderMessage),但反之亦然。

试着想想,例如,如果在你的代码中投掷InvalidCastException会被允许,如果你叫handler2c.Execute(new CommandMessage())会发生什么?

+0

我同意。你的解释很清楚。如果我可以执行该类型转换,那么当需要CreateOrderMessage时,我可以使用CommandMessage类型的参数在CreateOrderHandler2实例上调用Execute。这种方法似乎并不合理。当使用协变解决方案时,对Execute内部显式类型转换的要求似乎不再那么困难。 – 2012-02-15 18:22:08

0

接口ICommandMessageHandler1<T>ICommandMessageHandler2<T>彼此不相关。仅仅因为两者都有一个方法Execute不会使它们兼容。这将是duck-typing,这在c#中不受支持。

+0

我的例子错了。我没有尝试在ICommandMessageHandler1和ICommandMessageHandler2之间进行任何转换;他们只是我尝试过的两种不同的独立选择。倒数第二行应为:“var handler2c =(ICommandMessageHandler2 )handler2;” – 2012-02-15 18:13:26

0

也许我真的没有得到你想要做的,但是这对我没有任何typecasts正常工作。

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

这样做可行,但仅适用于仅有一种命令的情况,因为使用存在明确的类型转换。我想要做的是有一个通用的命令处理器,可以接收不同的命令消息,并将它们分派给相应处理程序类的实例。我可能会破解使用反射,但我试图使用控制反转来创建适当类的实例(我已经解决了这一部分),然后调用Execute方法而不指定派生类。我的例子中的“Covariant解决方案”起作用。 – 2012-02-15 18:27:17

相关问题