2

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.

5
  • A BaseViewModel<BaseModel> must be able to handle anything that inherits from BaseModel. You are passing it a ViewModelA, which can only handle a subset of things that inherit from BaseModel, i.e. objects that inehrit from ModelA. So the assignment is not allowed. Commented Sep 8, 2020 at 6:04
  • @JohnWu then is there any work around? Commented Sep 8, 2020 at 6:09
  • the workaround is: rethink your design. Is your list really of the base-type or do all your entitites are allways of the derived type? What is your cinsuming code that uses the collection? Commented Sep 8, 2020 at 6:17
  • Imagine this would be possible. Then users of your code could also write collection.Add(AnotherViewModel). Commented Sep 8, 2020 at 6:18
  • @HimBromBeere I am trying to allow collection.Add(AnotherViewModel) with the restriction to inherit BaseViewModel<T> where T : BaseModel. I understand it could be a design problem but I was curious why it does not working. Commented Sep 8, 2020 at 6:29

2 Answers 2

1

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
}
Sign up to request clarification or add additional context in comments.

Comments

0

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.

2 Comments

Thanks you helped me to understand this situation better, however if make collection to List<BaseViewModel<ModelA>>, I can not add ViewModelB : BaseViewModel<ModelB> to collection.
Obviously you cannot ViewModelB: BaseViewModel<ModelB> to the collection which takes an instance of BaseViewModel<ModelA> as the generic type is binded to ModelA. What you could do is that you can create an interface which is inherited in both ModelA and ModelB and then List<BaseViewModel<YourInterface>>. Then you can add any object which inherits the interface, refer to the previous answer posted by @Michael Randall

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.