C# Collection with class inheritance and generic type inheritance does not allow to add inherited class

Viewed 68

I encounter something maybe possible, but does not work.

Here is sample class,

abstract class BaseModel { }

abstract class BaseViewModel<T> where T : BaseModel
{
    protected T model;
}

class ModelA : BaseModel { }

class ViewModelA : BaseViewModel<ModelA> { }

and now I try to do

class Program
{
    static void Main(string[] args)
    {
        var collection = new List<BaseViewModel<BaseModel>>();
        collection.Add(new ViewModelA());
    }
}

Intuitively, it looks to be work. However it does not possible to add ViewModelA.

I want to know why this is not possible, and work around of this situation.

2 Answers

ViewModelA is not equal to BaseViewModel<BaseModel> even though you may think it is

This is because classes are invariant.

  • Invariance : means that you can use only the type originally specified

  • Covariance : enables you to use a more derived type than originally specified.

One way to achieve what you want (potentially) is to use a covariant interface parameter via the out generic modifier. However, it has certain limitations. Type parameters can only be used as a return type of interface methods and not used as a type of method arguments

Example

public interface IBaseViewModel<out T>  where T : BaseModel 
{ 
   ... 
}

abstract class BaseViewModel<T> : IBaseViewModel<T> where T : BaseModel 
{
   protected T model; 
}

class ModelA : BaseModel { }

class ViewModelA : BaseViewModel<ModelA> { }

static void Main(string[] args)
{
   var collection = new List<IBaseViewModel<BaseModel>>();
   collection.Add(new ViewModelA()); // this now works
}

I have different theory in simple words about your implementation, As per your implementation both your BaseViewModel<T> and BaseModel are abstract which mean you cannot instance an abstract class, meaning you cannot use BaseViewModel<BaseModel> baseModel = new ViewModelA();. When you adding to the collection you are trying to create an instance for the abstract class which violates the abstract class rules. I have modified your a little bit so that you can add to the collection.


abstract class BaseModel { }

abstract class BaseViewModel<T> where T : BaseModel
{
     protected T model;
}

class ModelA : BaseModel { }

class ViewModelA : BaseViewModel<ModelA> { }

static async Task Main(string[] args)
{
    var collection = new List<BaseViewModel<ModelA>>();
    collection.Add(new ViewModelA());
}

You can also make interface as per the previous answers to make decoupled class.

Related