1

I can't think a way to create view for inheritance hierarchy. If I create class hierarchy as in the code below, than I can't use methods and properties of the B class properly from BView.Set(...) without casting , because BView is inherited from AView. And Set method signature accepts variables of A type, but in BView I wish to Set B type variables. How can I solve my issue?

public class A
{
    public int id;
    public int received;
}
public class B:A
{
    public DateTime date;
//... other fields and properties 
    public void SomeMethod();
//... other methods
}

public interface IView<T>
{
   T Source{get;}
   void Init(T source);
   void Display(bool isOn);
   bool IsActive();
}
public class AView : IView<A>
{
   public A Source{get; private set;}
   public void Set(A source){
   Source = source;
   }
}
public class BView : AView
{
  ///???
}

Thank you. :3

1 Answers1

2

BView should not be a subtype of AView (just like a List<B> is not a subtype of a List<A>).

Why? The Liskov substitution principle states that a subtype can be used as "drop-in replacement" for a supertype. However, this is not satisified for BView and AView:

AView v = new AView();
v.Set(new A()); // that's fine

Now what happens if we substitute a BView?

AView v = new BView();
v.Set(new A()); // that should not be possible

In other words: BView does not satisfy the is-a relationship with AView, and, thus, BView should not be a subclass of AView.


Now, how do we solve this?

You could make your interface covariant, which would ensure that an IView<B> is automatically an IView<A>. To do that, you need to ensure that its type parameter T only appears in "out" positions:

public interface IView<out T>
{
   T Source{get;}
   // void Init(T source); -- we can't do that anymore
   void Display(bool isOn);
   bool IsActive();
}

The drawback: You won't be able to have your Init method anymore, which conveniently avoids the problem mentioned above. You can, however, set your source in the AView/BView constructor instead.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • 1
    Unfortunately, I can't use constructor because those view classes are inherited from MonoBehaviour class (one of the base classes in UnityEngine). UnityEngine doesn't let developer to create this type of classes through constructor. But let me thank you for wide answer. It's really helpful. :) Now, I think, I can find a way to solve problem. Perhaps, I'll try to divide interface on covariant and other one with setter. – HedgehogNSK Apr 28 '21 at 20:42