0

I try to use a interface realization through a generic class. Wondering on some abstraction magic, but get compiler errors with this approach.

cannot convert from 'GClass<PosClass>' to 'GClass<IPos>'

Here a simplified example:

//Very simple generic with only data access, no internal manipulation
class GClass<T> {
    T value;
    public GClass(T value) {
        this.value = value;
    }
    public T Get() {
        return value;
    }
}

//Simple interface
interface IPos {
    int GetPos();
}

//Simple realization
class PosClass : IPos {
    //Interface realization
    public int GetPos() {
        return 1;
    }
    public int GetAnotherImportantData() {
        return -1;
    }
}

class MainTrouble {
    public int DoUsingInterface(GClass<IPos> interfaceableItem) {
        //Do work using only interface methods
        return interfaceableItem.Get().GetPos();
    }

    public int DoUsingInheritance() {
        GClass<PosClass> item = new GClass<PosClass>(new PosClass());
        //Next line error me
        //cannot convert from 'GClass<PosClass>' to 'GClass<IPos>'
        var r = DoUsingInterface(item);
        //So, next I should execute a several methods using PosClass
        return item.Get().GetAnotherImportantData();
    }
}

Some suggestions? Maybe some tricky cast which I miss with it?

nvoigt
  • 75,013
  • 26
  • 93
  • 142
WildBraas
  • 25
  • 2

2 Answers2

2

This works and I'll add an explanation why at the bottom:

interface IGClass<out T>
{
    T Get();
}
class GClass<T> : IGClass<T>
{
    T value;
    public GClass(T value)
    {
        this.value = value;
    }
    public T Get()
    {
        return value;
    }
}

//Simple interface
interface IPos
{
    int GetPos();
}

//Simple realization
class PosClass : IPos
{
    //Interface realization
    public int GetPos()
    {
        return 1;
    }
    public int GetAnotherImportantData()
    {
        return -1;
    }
}

class MainTrouble
{
    public int DoUsingInterface(IGClass<IPos> interfaceableItem)
    {
        //Do work using only interface methods
        return interfaceableItem.Get().GetPos();
    }

    public int DoUsingInheritance()
    {
        GClass<PosClass> item = new GClass<PosClass>(new PosClass());

        var r = DoUsingInterface(item);

        return item.Get().GetAnotherImportantData();
    }
}

You want a GClass<PosClass> to be a GClass<IPos>. You know that works, because you know that GClass is "readonly". All PosClass are IPos, but obviously not all IPos are PosClass. So for the first to work, both would need to be true.

So you need to make sure your compiler knows what you know: that your class GClass is effectively read-only. Because then you only need the first part and that's always true. This is where the out keyword comes in. It's called covariance and only works on interfaces, that's why I added an extra interface.

Let's play this through with easier and more graphic examples. A Wolf is an Animal. A sheep is an Animal. But a List of sheep is not a list of animals... if it were, I could add a wolf to it, because a wolf is an animal. So what you need is an interface saying "Yes, they are all animals, on reading. Like IEnumerable<T>. An IEnumerable<Sheep> is an IEnumerable<Animal> because it's declared covariant, which is only possible because it only works in one direction: reading.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • I've always struggled to understand covariance, but I think I see what you're getting at. Is this essentially the list-equivalent of doing something like `IPos myVar = new PosClass();` and then only being able to work with `myVar` as if it were an `IPos` and not a `PosClass()` (since you would need a hard cast for that)? – Flater Nov 14 '17 at 09:12
  • 1
    Basically, yes. – nvoigt Nov 14 '17 at 09:17
-1

Based on this answer addressing a problem not far from yours, it will not work because there is no relation between GClass<IPos> and GClass<PosClass>. Indeed, a list of GClass<PosClass> is not a list of GClass<IPos>.

In order to help you understand better those problems, I suggest you to read this article about covariance and contravariance.

informaticienzero
  • 1,796
  • 1
  • 12
  • 22