【问题标题】:Covariance and Contravariance for Action Delegates行动代表的协变和逆变
【发布时间】:2012-09-26 09:01:05
【问题描述】:

我卡住了,为什么这不起作用:

  var childAction = new Action<CancelCommand>(blabla);
  Action<IDomainCommand> upperAction = (Action<IDomainCommand>) childAction;

如果 CancelCommand 实现了 IDomainCommand,为什么这不起作用? 顺便说一句:这是我在这里想要做的协变还是逆变? ;)

提前谢谢你 就是最好的 月桂酸

编辑 谢谢你们的快速回复!

我需要这个的问题是因为我构造了一个通用的动作模板。 我有以下界面:

IMessageBus.RegisterAction<T>(Action<T> registerAction) where T : IDomainCommand

因为我必须在运行时构建此操作,所以我的代码如下所示:

var genericExecuteAction = this.GetType().GetMethod("ExecuteCommandAction",
                                                            BindingFlags.NonPublic | BindingFlags.Instance).MakeGenericMethod(commandType);

var actionType = typeof(Action<>).MakeGenericType(commandType);
var @delegate = Delegate.CreateDelegate(actionType, this, genericExecuteAction);
var actionDelegate = (Action<DomainCommandBase>)@delegate;

messageBus.Register(actionDelegate);

问题是我需要转换它,以便我可以将它传递给这个方法。 你看? 我们使用的消息总线背后是使用 RX,不幸的是那里的所有方法都使用泛型,现在有一个非泛型重载。

【问题讨论】:

  • 协方差和反方差仅适用于接口,不适用于委托。
  • @Enigmativity:它也适用于代表。
  • 谢谢你,我找到了答案:我只需要通过反射调用注册方法,而不是强制转换它 =)

标签: c#


【解决方案1】:

Action 的类型参数是逆变的:您可以将Action&lt;object&gt; 分配给Action&lt;string&gt;,因为显然可以作用于任何对象的方法也可以作用于字符串。

您在这里所做的是尝试将适用于CancelCommand(派生类型)的方法计为适用于任何IDomainCommand(基本类型)的方法。这在逻辑上是错误的,所以编译器不允许你这样做——如果是这样,你可以调用 upperAction 并传递一个 DifferentTypeOfDomainCommand

【讨论】:

    【解决方案2】:

    这是逆变。这是行不通的,因为如果可以的话,你可以这样写:

    interface IDomainCommand { }
    class CancelCommand : IDomainCommand { }
    class AcceptCommand : IDomainCommand { }
    
            Action<CancelCommand> a1 = c => { };
            Action<IDomainCommand> a2 = a1;
    
            var acceptCommand = new AcceptCommand();
    
            a2(acceptCommand); // what???
    

    a2 指向a1,a1 不能接受AcceptCommand 参数,因为它不是CancelCommand

    【讨论】:

      【解决方案3】:

      等等,你为什么要这样做?

      您的childAction 接受CancelCommand 并对其进行处理。

      upperAction 可以在any IDomainCommand 上调用,而不仅仅是CancelCommand。假设以上编译;当我这样做时会发生什么

      upperAction(new EditCommand());
      

      class EditCommand : IDomainCommand 在哪里?

      如果你反过来尝试分配:

              var upperAction = new Action<IDomainCommand>(idc => { });
              Action<CancelCommand> childAction = upperAction;
      

      它有效,您甚至不需要演员表。任何Action&lt;IDomainCommand&gt; 都算作Action&lt;CancelCommand&gt;

      【讨论】:

      • 谢谢你们的快速回答。我通过编辑我的初始问题回答了您的问题。
      【解决方案4】:

      你忘记了这些代表是如何被使用的

      var childAction = new Action<CancelCommand>(blabla);
      Action<IDomainCommand> upperAction = (Action<IDomainCommand>) childAction;
      

      意味着upperAction 将在将来的某个时候被调用,传递某种形式的IDomainCommand 对象。你给它一个只能处理 CancelCommand 对象的函数。但是(可能)还有其他类实现了IDomainCommand,并且upperAction 可能会被其中任何一个调用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-03
        • 2015-02-09
        • 2012-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-03
        • 2014-03-06
        相关资源
        最近更新 更多