Reputation: 2974
I have been making some hamfisted attempts to apply correct OOP principles to my project. I have an abstract class called DocumentSection, and several classes deriving from it (DocumentSectionView, DocumentSectionText, etc). Similarly I have an abstract class (DocAction) with several classes deriving from it (DocumentActionReplaceByTag, DocumentSectionAppend, etc). Each DocumentSection has a DocumentAction within it.
My understanding of all this inheritance business is that by specifying a 'DocumentAction', this would allow any of those derived classes to be put in its place, and that any properties/methods from the base class would be available, as well as any specified in the concrete class I instantiate. So in the below example, I expected to be able to see the PerformAction method (leaving the virtual/override keywords out of the mix for now). And it is available.
However, because I went v.DocAction = new DocumentActionReplaceByTag();, I would also have expected my ReplaceActionFindText property to be visible.
Obviously I've got it wrong somewhere - any comments appreciated.
class Program
{
static void Main(string[] args)
{
DocumentSectionView v = new DocumentSectionView();
v.DocAction = new DocumentActionReplaceByTag();
// would like to go:
//v.DocAction.ReplaceActionFindText...
Console.ReadLine();
}
}
public abstract class DocumentSection
{
public abstract string GetContent();
public DocumentAction DocAction { get; set; }
}
public class DocumentSectionView : DocumentSection
{
public string ViewPath { get; set; }
public dynamic ViewModel { get; set; }
public override string GetContent()
{
return "test";
}
}
public abstract class DocumentAction
{
void PerformAction(StringBuilder sb, string content);
}
public class DocumentActionReplaceByTag : DocumentAction
{
public string ReplaceActionFindText { get; set; }
public void PerformAction(StringBuilder sb, string content)
{
sb.Replace(ReplaceActionFindText, content);
}
}
EDIT: I've marked an answer as correct, but thought I'd add the fruits of my further thought on this matter for those coming across this later:
a) As pointed out, my intentions were broadly right but my method wrong. Setting the 'Action's property from the Main method was not correct. In all cases, a a DocumentActionReplaceByTag requires the FindText so I placed it in the constructor:
public DocumentActionReplaceByTag(string replaceActionFindText)
{
this.ReplaceActionFindText = replaceActionFindText;
}
From then on, a constructor with 0 arguments will rightly fail, and prevent a case where the action is executed but no findtext is specified.
b) Polymorphism works fine now, because my extra property findtext has been populated, and running PerformAction will run correctly regardless of the action type.
Upvotes: 2
Views: 2266
Reputation: 62544
The v
reference type is of the DocumentSectionView
which is not aware of methods of the DocumentActionReplaceByTag
class even underlying instance is of DocumentActionReplaceByTag
as you've assigned it. You need to cast it to be able accesing derived class members:
((DocumentActionReplaceByTag)v.DocAction).ReplaceActionFindText
Also in some cases this is pretty fine when underlying instance could not be casted so some part of code should be skipped, then you can use exception-safe way of casting using as operator:
var typedAction = v.DocAction as DocumentActionReplaceByTag;
if (typedAction != null)
{
// accessing the typedAction.ReplaceActionFindText property
}
My suggestions are only to help you understand C# side of question, regarding overall design and approach please see BrokenGlass's answer.
Upvotes: 2
Reputation: 160992
Because you are assigning your derived class to a property with the type of the base class only the methods and properties of the base class will be available. And this makes sense since you could have assigned any instance of a class that derives from the base class - so any derived methods cannot be used in this context.
This is one of the OOP principles - your derived class instances may be used as an instance of a base class (but not the other way round)
Edit:
To elaborate on the solution proposed by @sll to cast to a particular derived class type: Don't do it! It is a workaround but not in the interest of the overall design.
If you have to cast to a derived type then you are violating the Liskov substitution principle meaning that any derived type should be usable in place of the base type - that's clearly not the case if you need a specific cast.
Rethink your design - do you really need a property with the base class type and if so are the methods currently only in one particular derived type better off being in the base type as well?
Upvotes: 2
Reputation: 161821
No, in your example, since DocAction
is only a DocumentAction
, you will only be able to see the properties of a DocumentAction
, no matter which derived type of DocumentAction
is used.
Upvotes: 0