1

i have a problem using Covariance and Contravariance with generic interface and list of base object. Here my class scheme (Reduced and simplified):

abstract class Human : IHuman {
     public string Ethnicity { get; set; }
  }

  interface IHuman {
   string Ethnicity { get; set; }
  }

  class Person : Human {
     public string Name { get; set; }
  }

  class Man : Person {
     protected string Age { get; set; }
  }

  class Women : Person {
     protected string AgeWrong { get; set; }
  }

  class Worker<T> : Person, IWorker<T> where T : IHuman {
     public string Code {
        get { return this.Name + this.Ethnicity; }
     }

     public Human Call(T who) {
        return null;
     }
  }

  interface IWork<out T> where T : IHuman {
     string Name { get; }
  }

  interface IWorkBase<in T> where T : IHuman {
     Human Call(T who);
  }

  interface IWorker<T> : IWork<T>, IWorkBase<T> where T : IHuman {  /*Empty*/ }

  static void Main(string[] args) {
     var list = new List<Human>();
     list.Add(new Man());
     list.Add(new Women());
     list.Add(new Worker<Man>());
     list.Add(new Worker<Women>());
     // Now i search by interface type
     var workers = from gr in list where (gr as IWorker<IHuman> != null) select (IWorker<IHuman>)gr;
     // here workers is Empty!!! :((( but if "i cast with Covariance"...like:
     var workersCov = from gr in list where (gr as IWork<IHuman> != null) select (IWork<IHuman>)gr;
     var w = new Worker<Man>();
     // Simple Test:
     Console.WriteLine(w is IWorker<Human>);
     Console.WriteLine(w is IWork<Human>);
  }

My question is why my cast works only with Covariance ? I have added simple test without LINQ.

Thanks.

  • I doubt `class Worker : Person, IWorker where T : IHuman` compiles. You don´t define `T` here. – MakePeaceGreatAgain Jan 27 '20 at 08:33
  • Your sample will not compile, `interface IWorker : IWork, IWorkBase where T : IHuman` require specifying generic type parameter, since `iWork` is generic itself – Pavel Anikhouski Jan 27 '20 at 08:34
  • I don't think `workersCov` will work since `IWork` is not implemented by anything, nor is `IWorkBase`. – ProgrammingLlama Jan 27 '20 at 08:35
  • 2
    Please try your simplified code before posting it. It's good you did simplify it to focus on the important bits, but this doesn't seem to work as intended. – ShamPooSham Jan 27 '20 at 08:36
  • 2
    Well, a `Worker` simply is **not** a `IWorker`, even though a `Man` is a `IHuman`. However that doesn´t mean the generics have the same inheritance-relation. – MakePeaceGreatAgain Jan 27 '20 at 08:40
  • Per default all generics are in-variant in .NET, which stops you from shooting into your own foot. This applies to structures, where your modify the content of the generic instance, e.g. adding instances to a list. If you do not modify that structure, you can surely cast one to another. However you have to tell the compiler that this cast is "safe", which is by using `out` on your interface-definition. – MakePeaceGreatAgain Jan 27 '20 at 08:59
  • Ok, i have change my code, and now it runs. Sorry. – The Overrider Jan 27 '20 at 08:59
  • @HimBromBeere yes i have seen, but IWorker is son of IWork with T of same Type... – The Overrider Jan 27 '20 at 09:02
  • Eeehm, what? `IWorker` is son of `IWorker`? No, it´s not, even though `Man` is "son" of `IHuman`. – MakePeaceGreatAgain Jan 27 '20 at 09:08
  • You can simplify the whole Main program down to this to make it simpler to understand: `var w = new Worker(); Console.WriteLine(w is IWorker); Console.WriteLine(w is IWork);` – Lasse V. Karlsen Jan 27 '20 at 09:08
  • @LasseV.Karlsen yes is the same. – The Overrider Jan 27 '20 at 09:25
  • @HimBromBeere i m speaking about the interface. IWorker include Covariance and Controvariance .. – The Overrider Jan 27 '20 at 09:38
  • Yeah, it is co-variant. But only because you told the compiler it is. By default generics are in-variant, unless you specify something different by `in` or `out`. Are you asking why generics are in-variant by default? – MakePeaceGreatAgain Jan 27 '20 at 09:54
  • @HimBromBeere, The thing I can't understand is why an object that implements both the covariance and contravariance interface is always handled by the compiler in-variant by default. – The Overrider Jan 27 '20 at 15:29

0 Answers0