2013-08-20 41 views
2

我想维护代表列表(这里是:“mCommandHandlers”)。因为他们是普通的代表,其实我定义的第二种类型的代表,这样我可以保持这样的名单:使用通用代理类型的委托类型和协方差

public delegate void CommandHandler<TCommand>(TCommand command) where TCommand : ICommand; 
public delegate void ICommandHandler(ICommand command); 
Dictionary<Type, ICommandHandler> mCommandHandlers; 

我会用编译时的优势,比如了解究竟是什么样的第一类我委托的实现正在使用TCommand:

RegisterHandler<ResourceCommand>((command) => 
{ 
     if (command != null) 
     { 
      ResourceManager.ResourceReceived(command.ResourceName, command.ResourceHash, command.ResourceData); 
     } 
}); 

里面RegisterHandler,我现在想做到以下几点:

public void RegisterHandler<TCommand>(CommandHandler<TCommand> handler) where TCommand : ICommand 
{ 
     mCommandHandlers.Add(typeof(TCommand), handler); 
} 

,但我得到了以下电子RROR消息:

错误3参数2:不能转换从 CommandHandler<TCommand>''ICommandHandler'

这是为什么?编译器是否应该看到实际上我的第一个委托类型要求参数至少是ICommand类型的,确保委托实例也符合第二个委托类型的签名?

回答

2

如果编译器看到,其实我的第一个委托类型需要的参数进行至少类型的ICommand,确保该委托实例符合第二委托的签名类型呢?

这里有两个问题。

首先,委托差异不允许将一个委托类型隐式引用转换为另一个委托类型 - 它允许您从兼容的现有委托创建一个新的代理实例。

其次,你有变化以错误的方式CommandHandler<TCommand>只接受特定命令的......而ICommandHandler将接受任何ICommand

所以想我们可以做到这一点:

CommandHandler<FooCommand> fooHandler = HandleFoo; 
ICommandHandler generalHandler = new ICommandHandler(fooHandler); 

然后我们可以称之为:

generalHandler(new BarCommand()); 

...你怎么会想到HandleFoo方法来应对呢?

ICommandHandlerCommandHandler<TCommand>转换为任何特定TCommand,因为当新的委托调用,这将永远是有效的。示例代码:

using System; 

delegate void CommandHandler<TCommand>(TCommand command) 
    where TCommand : ICommand; 
delegate void ICommandHandler(ICommand command); 

interface ICommand {} 

class Command : ICommand {} 

class Test 
{ 
    public static void Main() 
    { 
     ICommandHandler x = null; 
     CommandHandler<Command> y = new CommandHandler<Command>(x); 
    } 
} 

我建议你只需要改变你的字典:

Dictionary<Type, Delegate> mCommandHandlers; 

然后,当你需要调用任何特定的代表,你需要转换到正确的处理程序 - 这我想你会知道由于在那一点上的类型参数。或者你可以创建一个代理处理程序来执行演员,根据Jared的回答。

+0

我现在看到我是如何彻底改变协方差的想法的错误方式:)。我的具体代表不能坐在等待接受任何类型的ICommand,因为他们应该是mCommandHandlers的一部分。感谢您指出了这一点! – user1610325

2

问题是两个委托类型根本不兼容。为了完成这项工作,您将需要添加一个间接图层,该图层可以转换ICommandTCommand之间的参数。

public void RegisterHandler<TCommand>(CommandHandler<TCommand> handler) 
    where TCommand : ICommand 
{ 
    mCommandHandlers.Add(
    typeof(TCommand), 
    (command) => handler((TCommand)command); 
); 
} 
+0

谢谢,我也想过那种间接的,但我真的不知道到底为什么编译器不把他们看成兼容?由于代表应该是协变的,并且所有的代码都是要求协方差的。 – user1610325

+0

@ user1610325:看到我的答案 - 他们真的*不*兼容你想要的方式。他们兼容*其他*方式。总是考虑一下“不管参数如何,通过新的代理调用原始代理是否有效?” - 编译器试图确保始终有效。 –

+0

@ user1610325 - 'Func'代表在它们的输出中是协变的,但是它们的论点是相反的。无论如何,您的代表没有任何差异注释。 – Lee