Trouble with Interfaces, Inheritance, and Polymorphism
I have come to a point in my program where my polymorphism is broken. I realize that this is in my code, and I understand why; I'm just not sure how best to resolve it.
I have three model classes which have the same interface, but very significantly different implementations. I created an interface, and then three independent classes, which also derive from a ModelBase
class:
public interface IMyModel
{ ... }
public class MyModelA : ModelBase, IMyModels
{ ... }
public class MyModelB : ModelBase, IMyModels
{ ... }
public class MyModelC : ModelBase, IMyModels
{ ... }
So far fine and dandy.
I have a ViewModel base class, which takes a model as a constructor:
public abstract class MyViewModelBase
{
public MyViewModelBase(ModelBase Model)
this.model = Model;
}
Now where I am caught; I want to have a concrete ViewModel class that can accept any of the three Model classes above:
public class MyViewModel : MyViewModelBase
{
MyViewModel(IMyModel Model) : base (Model) // <- Invalid Polymorphism!
{
// More here
}
}
This doesn't work, because it is possible for an implementation of IMyModel to not be based on ModelBase. The argument cannot be safely passed to the base constructor.
I can see one solution being to create an abstract base class derived from ModelBase for these models with exception-throwing content, and using that as the type in my ViewModel. I had started with a base class, but found that almost every part had some difference! However, that seems like a lot of开发者_运维问答 work. Also, it won't ensure that derived classes implement everything (like an interface does). Finally, it seems to devalue the interface concept (indeed, I wouldn't need one anymore).
I don't see any way of marking the interface as saying that derived classes must have a specific base class. It would be nice if I could do this, but it's not allowed:
public interface IMyModel : MyModelBase
{ ... }
Is there a better way to do this?
Clarification:
I probably oversimplified the names here. I have other Models and ViewModels using the base classes, but not implementing the interface.
public class MyOtherModel : ModelBase // But not IMyModel!
{ ... }
public class MyOtherViewModel : MyViewModelBase
{
MyOtherViewModel(MyOtherModel Model) : base(Model) // This works
{ ... }
}
You could make your base class implement the interface, then inherit your implementation classes from the base class, marking the base class and methods as abstract (MustInherit/MustOverride in VB parlance). This would give you your polymorphism and guarantee the interface.
you could use generic constraints:
public class MyViewModel<T> : MyViewModelBase where T : IMyModel , ModelBase
{
MyViewModel(T model) : base (model) // T inherits ModelBase and implements IMyModel , so it is legal
{
// More here
}
}
Often (not always) when you find yourself building an Abstract Base Class it means you are trying to share both an interface and some common logic. Perhaps you can split these two things?
Move the common interface into IMyModels, and extract the common functionality into a separate class. Then include an instance of that helper class in each model. Essentially use composition to share functionality instead of inheritance.
Make ModelBase implement IMyModel. If ModelBase does not implement all methods of the interface, implement them as abstract methods.
To my understanding
- Models are stupid Data containers
- Interfaces describe behaviour and Functionality, not data.
So why bother putting interfaces on the models at all?
@Yochai : Good point, using Generics - saves code, but you must first declare base classes (only one per class, there's no mixed inheritance in C#), then any amount of interfaces, thus your code should be:
public class MyViewModel<T> : MyViewModelBase where T : ModelBase, IMyModel
精彩评论