Search code examples
c#collectionsgeneric-collectionsgeneric-constraints

How to constrain a generic collection to both a class name and an interface


Given public class BaseClass

that has derived classes, where a number of those follow the form

public class DerivedClass : BaseClass, ISpecificInterface

is there a way of specifying a collection that applies to just the derived classes that implement that interface?

For example, something like

public List<BaseClass where ISpecificInterface> myList; or

public List<BaseClass : ISpecificInterface> myList;


Solution

  • You can only constrain generic parameters, not generic arguments. So you'll need:

    public class DerivedClassWithInterfaceListContainer<TDerived>
        where TDerived : BaseClass, ISpecificInterface
    {
        public List<TDerived> MyList { get; set; }
    }
    

    You may want to inherit List<T> for this instead:

    public class DerivedList<TDerived> : List<TDerived>
        where TDerived : BaseClass, ISpecificInterface
    {    
    }
    

    And then you can use it as property type:

    public DerivedList<SomeDerivedClass> MyList { get; set; }
    

    Point being: you can only declare the list as containing one type. So if you want a list that can hold any class derived from BaseClass and implementing ISpecificInterface, you must do so in a method:

    // either ISpecificInterface _or_ BaseClass
    private List<ISpecificInterface> myList;
    
    public void AddToList<TDerived>(TDerived toAdd)
        where TDerived : BaseClass, ISpecificInterface
    {
        myList.Add(toAdd);
    }
    

    You could then combine this:

    public class DerivedList : List<ISpecificInterface>    
    {    
        public new void Add<TDerived>(TDerived toAdd)
            where TDerived : BaseClass, ISpecificInterface
        {
            this.Add(toAdd);
        }
    }
    

    But now someone can cast your DerivedList to IList<ISpecificInterface> and call Add() on that, with an object implementing ISpecificInterface but not inheriting from BaseClass.