0

The question arose about the need for keywords in variant delegates, if their events are in variant interfaces. The question only concerns the in and out keywords for delegates. See explanations below.

As soon as we make an interface with keyword in, a delegate with keyword out is required, why? After all, delagate without keyword out somehow has covariance, but only for methods, not delegate instances, which means I could put a method in event anyway, but not a delegate instance. But we cannot do this.

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //Step 1
            B MyMethodReturnB()
            {
                return new B();
            }

            //Step 2
            MyClass<A> MyClassForA = new MyClass<A>();
            MyClassForA.MyEvent += MyMethodReturnB; //In MyClass<A>() you can put data of type B through the method MyMethodReturnB and without keyword out in the delegate MyDelegateReturn

            //Step 3
            MyDelegateReturn<B> MyDelegateReturnForB = new MyDelegateReturn<B>(MyMethodReturnB);
            //MyClassForA.MyEvent += MyDelegateReturnForB; //But when we try to put data of type B through a delagate variable without keyword out, an error occurs, this is how variability works in delegates.
                                                           //Without the keywords in and out, one delegate cannot be assigned to another delegate

            //Step 4
            IMyInterface<B> IMyInterfaceForB = MyClassForA; //Through the interface IMyInterface<B> without keyword in, you cannot put MyClass<A>(), but if IMyInterface<B> with keyword in, then we can

            //Step 5            
            //As soon as we make an interface with keyword in, a delegate with keyword out is required, why?
            //After all, delagate without keyword out somehow has covariance, but only for methods, not delegate instances, which means I could put a method in event anyway, but not a delegate instance. But we cannot do this
        }
    }

    class A
    {

    }

    class B : A
    {

    }

    delegate T MyDelegateReturn</*out*/ T>(); //With keyword out all works

    interface IMyInterface<in T>
    {
        event MyDelegateReturn<T> MyEvent; //Error because delegate without keyword out
    }

    class MyClass<T> : IMyInterface<T>
    {
        public event MyDelegateReturn<T> MyEvent;
    }
}
  • Without `out`, the `IMyInterface`, which is only _supposed_ to _receive_ `T` values, would be _sending_ `T` values (i.e. to the delegate). See duplicate for an extensive discussion of variance and how it works with delegates and interfaces. – Peter Duniho Jul 16 '21 at 00:53
  • Supposing classes `Mammal : Animal`, and suppose we have an object implementing `IMyInterface`. Since it has a contravariant `in` type parameter, it could be it really implements `IMyInterface`. Therefore `myobj.MyEvent` would return `Animal` in that case. So it should be allowed to return either `Mammal` or `Animal`, which means it *must* be declared covariant `out`. – Charlieface Jul 16 '21 at 01:18
  • What prevents you from returning both types from events as in step two without using the out keyword? –  Jul 16 '21 at 01:53
  • Because it won't be expected. So let's say you now take the delegate contained in `MyEvent` and pass it to code expecting `MyDelegateReturn`, this code expects the return type to be `Mammal` but it isn't, it's `Animal`. If you declare it `out` then you can *only* pass it to code that is *also* declared `out` and therefore *expects* `Mammal` *or a less-derived type* such as `Animal`. – Charlieface Jul 16 '21 at 17:08
  • Basically, you **always** need to match the same co- or contra-variance (or non-variance) to get a type to match, and when you use delegates or events, the variance flips from `out` to `in` or from `in` to `out`. This post might help you also https://stackoverflow.com/questions/48177452/covariance-and-contravariance-with-func-in-generics/ – Charlieface Jul 16 '21 at 17:09

0 Answers0