Covariance and contravariance real world example

后端 未结 9 1898
醉梦人生
醉梦人生 2020-11-22 16:57

I\'m having a little trouble understanding how I would use covariance and contravariance in the real world.

So far, the only examples I\'ve seen have been the same o

相关标签:
9条回答
  • 2020-11-22 17:47

    The converter delegate helps me to visualise both concepts working together:

    delegate TOutput Converter<in TInput, out TOutput>(TInput input);
    

    TOutput represents covariance where a method returns a more specific type.

    TInput represents contravariance where a method is passed a less specific type.

    public class Dog { public string Name { get; set; } }
    public class Poodle : Dog { public void DoBackflip(){ System.Console.WriteLine("2nd smartest breed - woof!"); } }
    
    public static Poodle ConvertDogToPoodle(Dog dog)
    {
        return new Poodle() { Name = dog.Name };
    }
    
    List<Dog> dogs = new List<Dog>() { new Dog { Name = "Truffles" }, new Dog { Name = "Fuzzball" } };
    List<Poodle> poodles = dogs.ConvertAll(new Converter<Dog, Poodle>(ConvertDogToPoodle));
    poodles[0].DoBackflip();
    
    0 讨论(0)
  • 2020-11-22 17:48
    // Contravariance
    interface IGobbler<in T> {
        void gobble(T t);
    }
    
    // Since a QuadrupedGobbler can gobble any four-footed
    // creature, it is OK to treat it as a donkey gobbler.
    IGobbler<Donkey> dg = new QuadrupedGobbler();
    dg.gobble(MyDonkey());
    
    // Covariance
    interface ISpewer<out T> {
        T spew();
    }
    
    // A MouseSpewer obviously spews rodents (all mice are
    // rodents), so we can treat it as a rodent spewer.
    ISpewer<Rodent> rs = new MouseSpewer();
    Rodent r = rs.spew();
    

    For completeness…

    // Invariance
    interface IHat<T> {
        void hide(T t);
        T pull();
    }
    
    // A RabbitHat…
    IHat<Rabbit> rHat = RabbitHat();
    
    // …cannot be treated covariantly as a mammal hat…
    IHat<Mammal> mHat = rHat;      // Compiler error
    // …because…
    mHat.hide(new Dolphin());      // Hide a dolphin in a rabbit hat??
    
    // It also cannot be treated contravariantly as a cottontail hat…
    IHat<CottonTail> cHat = rHat;  // Compiler error
    // …because…
    rHat.hide(new MarshRabbit());
    cHat.pull();                   // Pull a marsh rabbit out of a cottontail hat??
    
    0 讨论(0)
  • 2020-11-22 17:49

    The in and out keywords control the compiler's casting rules for interfaces and delegates with generic parameters:

    interface IInvariant<T> {
        // This interface can not be implicitly cast AT ALL
        // Used for non-readonly collections
        IList<T> GetList { get; }
        // Used when T is used as both argument *and* return type
        T Method(T argument);
    }//interface
    
    interface ICovariant<out T> {
        // This interface can be implicitly cast to LESS DERIVED (upcasting)
        // Used for readonly collections
        IEnumerable<T> GetList { get; }
        // Used when T is used as return type
        T Method();
    }//interface
    
    interface IContravariant<in T> {
        // This interface can be implicitly cast to MORE DERIVED (downcasting)
        // Usually means T is used as argument
        void Method(T argument);
    }//interface
    
    class Casting {
    
        IInvariant<Animal> invariantAnimal;
        ICovariant<Animal> covariantAnimal;
        IContravariant<Animal> contravariantAnimal;
    
        IInvariant<Fish> invariantFish;
        ICovariant<Fish> covariantFish;
        IContravariant<Fish> contravariantFish;
    
        public void Go() {
    
            // NOT ALLOWED invariants do *not* allow implicit casting:
            invariantAnimal = invariantFish; 
            invariantFish = invariantAnimal; // NOT ALLOWED
    
            // ALLOWED covariants *allow* implicit upcasting:
            covariantAnimal = covariantFish; 
            // NOT ALLOWED covariants do *not* allow implicit downcasting:
            covariantFish = covariantAnimal; 
    
            // NOT ALLOWED contravariants do *not* allow implicit upcasting:
            contravariantAnimal = contravariantFish; 
            // ALLOWED contravariants *allow* implicit downcasting
            contravariantFish = contravariantAnimal; 
    
        }//method
    
    }//class
    
    // .NET Framework Examples:
    public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable { }
    public interface IEnumerable<out T> : IEnumerable { }
    
    
    class Delegates {
    
        // When T is used as both "in" (argument) and "out" (return value)
        delegate T Invariant<T>(T argument);
    
        // When T is used as "out" (return value) only
        delegate T Covariant<out T>();
    
        // When T is used as "in" (argument) only
        delegate void Contravariant<in T>(T argument);
    
        // Confusing
        delegate T CovariantBoth<out T>(T argument);
    
        // Confusing
        delegate T ContravariantBoth<in T>(T argument);
    
        // From .NET Framework:
        public delegate void Action<in T>(T obj);
        public delegate TResult Func<in T, out TResult>(T arg);
    
    }//class
    
    0 讨论(0)
提交回复
热议问题