1

So I have some Java code that makes extensive use of generics that compiles just fine. I ported it over to C# as follows:

interface IFoo1 { }
interface IFoo2 { }

interface IBar<T, K>
    where T : IFoo1
    where K : IFoo2 {
    List<T> GetFoo1s();
    void AddAFoo1(T foo1);

    List<K> GetFoo2s();
    void AddAFoo2(K foo2);
}

interface IBlip<T>
    where T : IBar<IFoo1, IFoo2> {
    T DoBlip(string input);
    void DoBlip2(T input);
}

interface IConverter<T, K>
    where T : IBar<IFoo1, IFoo2>
    where K : IBar<IFoo1, IFoo2> {
    K Convert(T input);
}

class FooA1 : IFoo1 { }
class FooB1 : IFoo1 { }

class FooA2 : IFoo2 { }
class FooB2 : IFoo2 { }

class BarA : IBar<FooA1, FooA2> {
    public List<FooA1> GetFoo1s() { return null;  }
    public void AddAFoo1(FooA1 foo1) { }
    public List<FooA2> GetFoo2s() { return null; }
    public void AddAFoo2(FooA2 foo2) { }
}

class BarB : IBar<FooB1, FooB2> {
    public List<FooB1> GetFoo1s() { return null; }
    public void AddAFoo1(FooB1 foo1) { }
    public List<FooB2> GetFoo2s() { return null; }
    public void AddAFoo2(FooB2 foo2) { }
}

class BlipA : IBlip<BarA> {
    public BarA DoBlip(string input) { return null; }
    public void DoBlip2(BarA input) { }
}

class BlipB : IBlip<BarB> {
    public BarB DoBlip(string input) { return null; }
    public void DoBlip2(BarB input) { }
}

class ConverterImplementation : IConverter<BarA, BarB> {
    public BarB Convert(BarA input) {
        return null;
    }
}

When I compile this, it complains that, for example, with the ConverterImplementation, that BarA cannot be implicitly converted to IBar. I guess there's something that I'm fundamentally missing here. Could someone shed some light on it? Thanks.

tnw
  • 13,521
  • 15
  • 70
  • 111
Chris Bush
  • 229
  • 1
  • 13
  • 4
    Do a search on C#, tigers and giraffes. Trust me on this one. – Anthony Pegram Mar 22 '13 at 21:04
  • For the record (and the linked pages): http://stackoverflow.com/questions/4669858/simple-examples-of-co-and-contravariance and http://stackoverflow.com/questions/5881677/why-cant-i-cast-from-a-listmyclass-to-listobject – arcain Mar 22 '13 at 21:22
  • Sorry if the question is similar to others. This example seemed a little more involved than some of the other examples I've seen out there, so was just seeing if anybody had any tips or tricks. I think ultimately I'm just going to have to redesign my API. – Chris Bush Mar 22 '13 at 22:04

2 Answers2

4

Generic type parameters are by default neither contravariant nor covariant, but can be made one or the other via the "in" and "out" keywords.

In the case of IBar<T, K>, both type parameters are used as both inputs and outputs, so you cannot make them either contravariant or covariant. If you refactored it into two interfaces, one in which T is used only for input and K only for output, and one in which T is used only for output and K only for input, then you could make each type parameter covariant or contravariant based on its usage.

j__m
  • 9,392
  • 1
  • 32
  • 56
2

IBar is not a read only interface, therefore you may not achieve convariance in C#. You need to refactor and extract a read only interface, e.g. ReadOnlyBar, and do convariance on that interface. (disclaimer - not an expert on C#)

On the other hand, Java's wildcard can turn an interface to read-only and convariant interface, so IBar<? extends Animal> is read-only convariant, and IBar<? extends Tiger> is a subtype of it. That's cool and all, until your code is littered with lots of wildcards.

ZhongYu
  • 19,446
  • 5
  • 33
  • 61