How to restrict a generic type parameter to child classes?

Viewed 106

I'm trying to do something like that:

    internal class ConcreteLinkedItem : GenericLinkedItem<ConcreteLinkedItem>
    { //Specific methods which use GenericLinkedItem
    }
    internal class GenericLinkedItem<TItem>
        where TItem : GenericLinkedItem<TItem>
    {
        public TItem? Next { get; private set; }
        public TItem? Previous { get; private set; }

        public void AttachNext(TItem item)
        {
            Next = item;
//Error CS0266 Cannot implicitly convert type 'GenericLinkedItem<TItem>' to 'TItem'...  
            item.Previous = this; 

        }
    }

The only solutions I have in mind are:

  • Explicit cast (no compile time type check)
            item.Previous = (TItem)this;

  • Abstract method to retrieve this (boilerplate)
        public void AttachPrevious(TItem item)
        {
            Previous = item;
            item.Next = GetThis();
        }

        protected abstract TItem GetThis();
  • Use composition instead of inheritance aka System.Collections.Generic.LinkedList (even more boilerplate).

Any beautiful ideas?

PS: For now I go with "protected abstract TItem This { get; }". Which still is not what I want as you can mix different item (node) types, but is at least type safe:

    internal class ConcreteLinkedItem : GenericLinkedItem<ConcreteLinkedItem>
    { //Specific methods which use GenericLinkedItem
        protected override ConcreteLinkedItem This => this;
    }
    internal abstract class GenericLinkedItem<TItem>
        where TItem : GenericLinkedItem<TItem>
    {
        public TItem? Next { get; private set; }
        public TItem? Previous { get; private set; }

        public void AttachPrevious(TItem item)
        {
            Previous = item;
            item.Next = This;
        }

        protected abstract TItem This { get; }
    }
1 Answers

use an interface:

internal class ConcreteLinkedItem : IGenericLinkedItem<ConcreteLinkedItem>
{
    public ConcreteLinkedItem? Next { get; private set; }
    public ConcreteLinkedItem? Previous { get; private set; }

    public void AttachNext(ConcreteLinkedItem item)
    {
        Next = item;
        item.Previous = this;
    }
}

internal interface IGenericLinkedItem<TItem> where TItem : IGenericLinkedItem<TItem>
{
    TItem? Next { get; }
    TItem? Previous { get; }

    void AttachNext(TItem item);
}

or if you must have a generic class then you'll need a cast:

internal class ConcreteLinkedItem : BaseLinkedItem<ConcreteLinkedItem>
{
    //Specific methods which use GenericLinkedItem
}

internal class BaseLinkedItem<TItem> : IGenericLinkedItem<TItem> where TItem : BaseLinkedItem<TItem>
{
    public TItem? Next { get; private set; }
    public TItem? Previous { get; private set; }

    public void AttachNext(TItem item)
    {
        Next = item;
        item.Previous = (TItem)this;
    }
}

internal interface IGenericLinkedItem<TItem> where TItem : IGenericLinkedItem<TItem>
{
    TItem? Next { get; }
    TItem? Previous { get; }

    void AttachNext(TItem item);
}
Related