使用通用委托类型委派类型和协方差

时间:2013-08-20 21:20:52

标签: c# delegates covariance

我想维护一个代表列表(这里:“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);
}

但是我收到以下错误消息:

  

错误3参数2:无法转换   CommandHandler<TCommand>'来   'ICommandHandler'

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

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:2)

  

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

这里有两个问题。

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

其次,你得到的方差是错误的,CommandHandler<TCommand>只会接受特定的类型的命令...而ICommandHandler会接受任何 ICommand

假设我们可以这样做:

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

然后我们可以打电话:

generalHandler(new BarCommand());

...您希望HandleFoo方法如何应对?

对于任何特定的ICommandHandler 是从CommandHandler<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的回答创建一个执行演员表的代理处理程序。