简体   繁体   中英

Generic collection of restricted types

I have a class who requires the following definition :

public class Table<T> : ObservableCollection<T> where T : IRowDef, new()

I want to create a collection of it and map types with the instances. So I try :

public sealed class TableCollection : IEnumerable<Table<IRowDef>>
{
   private Dictionary<Type, Table<IRowDef>> _tableDictionary;

   public Table<IRowDef> GetTable<T>() where T : IRowDef, new()
   {
        Table<IRowDef> table = null;

        if (_tableDictionary.ContainsKey(typeof(T)))
        {
            table = _tableDictionary[typeof(T)];
        }
        else
        {
            table = new Table<IRowDef>();
            _tableDictionary.Add(typeof(T), table);
        }

        return table;
   }

   ...
}

But I can't make it work. The following lines and several others gives the same error :

private Dictionary<Type, Table<IRowDef>> _tableDictionary;

The error, translated, tells IRowDef must be non abstract and have a parameterless constructor. I know it comes from the "new()" type restriction on the Table class definition, but it is required by the code inside this class. I known I could solve this by using a specific class type who would contains a parameter less constructor, for example :

private Dictionary<Type, Table<ClientTable>> _tableDictionary;

But different types of table must be supported and is the reason why all of them implements IRowDef.

Does anybody knows how I can solve this?

The problem is that you need a collection of tables, but a Table<X> is not compatible with a Table<Y> and a WhateverCollection<Table<X>> is not compatible with a WhateverCollection<Table<Y>> , even if X is an interface type and Y implements this interface.

Why it that? Asssume that you have

List<IAnimal> animals = new List<Elefant>();
animals.Add(giraffe); // Ooops!

Put that in your pipe and smoke it!

 // DOES NOT WORK! T<Base> b = new T<Derived>(); // T<Derived> is not assignment compatible to T<Base>!!! 

but

Base b = new Derived(); // OK

The trick is to have two table classes: One non-generic base class and a derived generic class:

public abstract class Table
{}

public class Table<T> : Table
     where T : IRowDef, new()
{
     private readonly ObservableCollection<T> _rows = new ...;
}

Now you can declare a

private Dictionary<Type, Table> _tableDictionary;

Or if you want to stick to deriving from the an observable collection, declare a (non-generic!) ITable interface instead of a Table base class and let Table<T> implement ITable then declare the dictionary as Dictionary<Type, ITable> .

You could remove the new() constraint and use Activator.CreateInstance<T>() to create new objects. This moves the check from compile time to runtime. The C# compiler translates new T() to a Activator.CreateInstance call anyway.

Olivier Jacof-Descombes proposed one of possible approaches. The other one (applicable only if you can modify Table class):

public interface ITable
{
    //Some needed methods, f,e,
    IRowDef GetSth();
}

then:

public class Table<T> : ..., ITable where T : IRowDef, new()
{
    IRowDef ITable.GetSth()
    { 
         return (IRowDef)this.GetSthImplInsideTable(); // cast is optional
    }

    public T GetSthImplInsideTable() { /* impl */ }
}

You could use it as:

private Dictionary<T, ITable> _tablesDict;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM