我不确定我是否遗漏了什么,或者只是不了解编译器的工作方式......基本上,我希望能够传递带有约束参数的操作,而无需重新转换它。将以下代码与有效的强制转换一起获取:
private readonly Dictionary<Type, Action<ICommand>> _dictionary = new Dictionary<Type, Action<ICommand>>();
public void Register<TCommand>(Action<TCommand> action) where TCommand : ICommand
{
_dictionary.Add(typeof(TCommand), x => action((TCommand)x));
}
下面抛出一个错误,并说 TCommand 的参数不能作为 ICommand 传递
public void Register<TCommand>(Action<TCommand> action) where TCommand : ICommand
{
_dictionary.Add(typeof(TCommand), action);
}
我做错了什么,还是 where 约束只能由方法签名理解,而代码的其余部分忽略了此指令?
Action
的第一个通用参数是逆变而不是协变。 Action<ICommand>
不是Action<SomeCommand>
的子类型。 事实上,情况正好相反。 Action<SomeCommand>
是Action<ICommand>
的一个子类型!
现在,您有一个只能执行的操作 SomeCommand
。 如果你能把它投到Action<ICommand>
那么有人可以EvilCommand
传递,但EvilCommand
不是一种SomeCommand
。 另一方面,如果您编写了一个可以接受任何类型的ICommand
的方法,那么显然您可以假装它是一个只接受SomeCommand
对象的方法,因为您知道所有这些对象都将实现ICommand
。
第一个解决方案之所以有效,是因为您引入了参数的显式强制转换。 您正在创建一个新方法,该方法执行所需的特定类型检查。 除了工作之外,在这种特定情况下,它也是正确的设计;这是罕见的情况之一,你知道对象实际上是一个TCommand
实例,而不仅仅是一个ICommand
,但编译器无法证明这一点,并且没有任何真正好的方法来重新设计应用程序,以便它可以静态验证这一点。