Search code examples
c#inheritancetype-conversionabstract-classtype-parameter

Handling classes inherent from abstract class and type parameter


I have a base abstract class and its abstract type parameter as:

public abstract class Database<T> where T : DatabaseItem, new() {
  protected List<T> _items = new List<T> ();
  protected virtual void Read (string[] cols)   {
    T item = new T ();
    ...
}

public abstract class DatabaseItem {
  ...
}

Then I have number of children classes inherent from it:

public class ShopDatabase : Database<ShopItem> {}
public class ShopItem : DatabaseItem {}

public class WeaponDatabase : Database<WeaponItem> {}
public class WeaponItem : DatabaseItem {}

...

Now, I want to put a Dictionary to keep track of the databases and a method to return them using DatabaseItem type, something like this:

Dictionary<Type, Database<DatabaseItem>> databases;

public static Database<T> GetDatabase<T> () where T: DatabaseItem {
  return databasebases [typeof (T)];
}

Then it gave me "'T' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T'" error because DatabaseItem is abstract. I made DatabaseItem as non-abstract type, the error is gone but still lot of type conversion errors came out...

Found a similar question but I still not understand...

What's the better solution/structure to achieve this?


Solution

  • The simplest you can do is to store Database<T> descendants as objects:

    static class DatabaseManager
    {
        private static Dictionary<Type, object> databases = new Dictionary<Type, object>();
    
        // ....
    
        public static Database<T> GetDatabase<T>()
            where T : DatabaseItem, new()
        {
            return (Database<T>)databases[typeof(T)];
        }
    }
    

    Even if you turn DatabaseItem to non-abstract class with parameterless constructor, this won't help either, because typeof(Database<ShopItem>) != typeof(Database<DatabaseItem>).

    Note, that generic constraints for GetDatabase<T> method must be the same as for Database<T> class.

    Upd.

    is there a way to know what type parameter a class using? e.g. giving ShopDatabase, I want to get ShopItem. I need it when I initialize databases dictionary

    Use reflection:

    var databaseItemType = typeof(ShopDatabase).BaseType.GetGenericArguments()[0];
    

    For cases like this:

    class FooDatabase : Database<FooItem> {}
    class BooDatabase : FooDatabase {}
    // etc...
    

    you'll need to loop through inheritance tree to find Database<FooItem>.