Covariance and Contravariance for Action Delegates

前端 未结 4 1160
长发绾君心
长发绾君心 2021-01-27 10:59

I\'m stuck, why isn\'t this working:

  var childAction = new Action(blabla);
  Action upperAction = (Action

        
相关标签:
4条回答
  • 2021-01-27 11:09

    This is contravariance. This isn't working, because if this will be possible, you could write something like this:

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

    a2 points to a1, and a1 can't accept AcceptCommand argument, because it is not a CancelCommand.

    0 讨论(0)
  • 2021-01-27 11:13

    Your forgetting how these delegates are being used

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

    means that upperAction will at some point in the future be called, passing some form of an IDomainCommand object. You are giving it an function which can only handle CancelCommand objects. But there are (probably) other classes which implement IDomainCommand, and upperAction could potenially be called with any of them.

    0 讨论(0)
  • 2021-01-27 11:14

    Hang on, why do you want this to work?

    Your childAction is something that accepts a CancelCommand and does something with it.

    But upperAction could be invoked on any IDomainCommand, not just a CancelCommand. Supposing the above compiled; what should then happen when I do

    upperAction(new EditCommand());
    

    where class EditCommand : IDomainCommand ?

    If you attempt an assignment the other way round:

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

    it works and you don't even need a cast. Any Action<IDomainCommand> counts as an Action<CancelCommand>.

    0 讨论(0)
  • 2021-01-27 11:31

    The type parameter of Action is contravariant: you can assign an Action<object> to an Action<string> because obviously a method that can work on any object can also work on a string.

    What you do here is trying to bill a method that works on a CancelCommand (derived type) as a method that works on any IDomainCommand (base type). This is logically wrong, so the compiler doesn't let you do it -- if it did, you could just invoke upperAction passing it a DifferentTypeOfDomainCommand.

    0 讨论(0)
提交回复
热议问题