你能使用Generic Action<>或者Func<>带where子句的参数?

时间:2014-04-28 19:45:38

标签: c# lambda delegates

我不确定我是否遗漏了某些东西或者只是不理解编译器的工作方式...基本上我希望能够通过约束参数传递一个动作,而不必重新制作它。使用下面的代码与演员工作:

    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 约束只能被方法签名理解,其余的代码会忽略这个指令吗?

1 个答案:

答案 0 :(得分:2)

Action的第一个通用参数是逆变而非协变Action<ICommand>不是Action<SomeCommand>的子类型。事实上,它是相反的方式。 Action<SomeCommand> Action<ICommand>的子类型!

现在你的行动只能采用SomeCommand。如果您可以将其投放到Action<ICommand>,则有人可以传入EvilCommand,但EvilCommand不是SomeCommand的类型。另一方面,如果您编写的方法可以接受任何类型的ICommand,那么显然您可以假装它是一种只接受SomeCommand个对象的方法,因为您 know 所有这些都将实现ICommand

您的第一个解决方案有效,因为您正在引入参数的显式强制转换。您正在创建新方法,该方法执行所需的特定类型检查。除了工作之外,它也是这种特殊情况下的正确设计;它是知道对象实际上是TCommand实例的罕见情况之一,而不仅仅是ICommand,但编译器可以&#39;证明这一点,并没有任何真正好的方法来重新设计应用程序,以便它可以静态验证。