Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Base class inheriting from Generic Interface

For some reason I am battling to implement a property from a generic interface by using a generic base class as follows:

public interface IParent<TChild> where TChild : IChild
{
    TChild Child { get; }
}

public interface IChild { }

Then I have a base class:

public class ParentBase<TChild> : IParent<TChild> where TChild : IChild
{
    private TChild _child;
    public ParentBase(TChild child)
    {
        this._child = child;
    }
    #region IParent<TChild> Members

    public TChild Child
    {
        get { return _child; }
    }

    #endregion
}

Now I have a new Parent Derivative and Child object as follows:

public class MyChild : IChild { }

public class MyParent : ParentBase<MyChild>, IParent<IChild> 
{
    public MyParent(MyChild child)
        : base(child)
    {
    }
}

I want to instantiate it and get the abstract (interface type) to pass to consumers as follows:

IParent<IChild> parent = new MyParent(new MyChild());

But for some reason I cannot implement the TChild correctly, even though I have defined the property public TChild Child on the ParentBase, the compiler says it is not implemented, even if i try implement explicitly. As you can see the constraints are all the way through to the base class.

like image 483
Andre Avatar asked Apr 17 '12 10:04

Andre


2 Answers

You are deriving MyParent from ParentBase<MyChild> and IParent<IChild>. There is no implementation for

IParent<IChild> { IChild Child{get; } }

Adding an explicit implementation will allow your original code to compile

public class MyParent : ParentBase<MyChild>, IParent<IChild>
{
    public MyParent(MyChild child)
        : base(child)
    {
    }

    #region Implementation of IParent<IChild>

    IChild IParent<IChild>.Child
    { 
        get { return base.Child; }
    }

    #endregion
}

If you also make IParent covariant like this:

public interface IParent<out TChild> where TChild : IChild
{
    TChild Child { get; }
}

then you can now do this

IParent<IChild> parent = new MyParent(new MyChild());

or

ParentBase<MyChild> parent2 = new MyParent(new MyChild());

and

IParent<IChild> parent3 = parent2;

And as pointed out in the answer by @svick, with covariance you can then simplify by not deriving from IParent<IChild> and removing the explicit interface implementation.

like image 173
Phil Avatar answered Nov 15 '22 07:11

Phil


This is exactly where generic variance is useful. If you marked your IParent interafce as covariant:

public interface IParent<out TChild> where TChild : IChild

and removed the explicit derivation of IParent<IChild> from MyParent:

public class MyParent : ParentBase<MyChild>

then MyParent can be treated as if it implemented IParent<IChild>. So for example, the following code would work:

MyParent parent = new MyParent(new MyChild());
IParent<IChild> iParent = parent;
like image 34
svick Avatar answered Nov 15 '22 06:11

svick