Libor Bešenyi (Solution Architect)
To be able to continue with the syntactic analysis, it is probably inevitable to mention the so called inheritance (although I originally wanted to deal with it later). A large group of programmers consider inheritance to be the best feature of OOP, which I totally disagree with. I think this feature is overrated because the other features may be a bit more complicated, so many pay less attention (and effort) to them - and they make a mistake!
What is inheritance about anyway?
Class can share variables, methods, virtually all parts. The advantage is that if we identify these frequently recurring parts, we save time writing them, but also, when correcting an error at the lowest level, we do not have to intervene in derived classes and correct everything again.
Everybody must come to terms with the question of duplication. Although duplication means more work, sometimes it is unavoidable for complex systems, where it is better to copy the existing code and implement new parts as in a new class (regardless of the original class). In this way we can avoid critical system components, as similarly to the way fixing bugs is transferred to all classes, introducing new errors can cause a collapse in all classes that inherit it. In very complex systems it is sometimes difficult to make an analysis of the impact of intervention in the class, it may be equally challenging to test the change. An example would be a code compiled for multiple platforms (e.g. COM Delphi, trimmed .Net 1 in PocketPC and Web server on .Net 3.5). Each platform represents its own rules and modification of some classes at "low" level may cause an error in the translation of the COM version (forced translation) or logical errors in running programs.
Such systems tend to have too many classes and exemptions for "the given" case, which speaks volumes about the class not being appropriately designed and the fact that the changes over the years made the code into something the members of the team fear to interfere with, because no one knows how it actually works. Therefore, please always consider whether the benefits of inheritance outweigh the negatives in individual cases, if you plan to use only this feature of OOP. In the part on polymorphism, we will deal with an example of inappropriate implementation of the class hierarchy.
If we want to derive one class from another, we must define it as follows:
public class BasicClass { public string Text; } public class DerivedClass : BasicClass { }
The second, derived class has defined its initial class as BasicClass. Now, if we create an instance of the class DerivedClass, despite the fact that it does not define variable Text, this variable will be usable due to the properties of inheritance:
private void button1_Click(object sender, EventArgs e) { DerivedClass class = new DerivedClass(); class.Text = "class"; // Text variable is available }
You say it’s nothing ground-breaking? I agree :) How can it help us? A good example of inheritance are components of VLC from Delphi (where we find readily available source codes, providing incredible help in development, although you can download the codes, but I don’t do it unless it’s necessary).
Then what's going on with the components? Every object that we can see on the form has certain characteristics that are continuously repeated. There are incredibly many of those objects (textbox, combobox, listbox, label, checkbox, radiobutton ...) and a property, such as the position of a form is a common part for each object. We will introduce these characteristics on a similar example that is generation of a HTML code.
But before that, I would like to say that inheritance and base classes (ancestors) create ultimately a single class (the highest) and the features are as if overlaid on a film. If we wrote each class on a transparent film and its derived class to another film, the resulting class is actually the image formed by placing the films on top of each other! Thus, by inheritance we stock the class, it has nothing to do with run-time result (as it only helps the programmer).
Let's see how we could analyse our HTML code generator. We know that there are two types of elements, block and non-block:
<block></block> <nonblock />
In our case, HTML generator will have easy functionality (due to demonstration of individual practices). The inheritance could therefore be designed as follows: there is a common HTMLElement class and the derived classes HtmlElementBlock and HtmlElementNonblock. From these classes, we will inherit real elements HtmlDiv, HtmlSpan, HtmlTable, HtmlInput ... so that hierarchy could look like this:
Now we must think about the importance of having such an extensive layer of inheritance. If we omitted HTMLElement, we would have to edit everything twice for block and non-block elements (name, style, ...), it would also create a problem of polymorphism (will be discussed later). Well, shall we annul the second layer and redefine only block element as was IsBlockElement? Here, however, we would come across another irregularity, namely that block elements can contain other elements, and then we would be forced to deal with exceptions if we wanted to add an element in non-blocking element to the code. Such conditions are usually a sign of faulty architecture of classes. On the other hand, the last layer appears to be an unnecessary duplication, because the only thing that would define these objects, is the name. Therefore, for now we cancel the last layer and add "name" in the base class, which is inherited:
public class HtmlElement { public string Name; } public class HtmlElementBlock : HtmlElement { } public class HtmlElementNonblock : HtmlElement { }
In order to be able to notice the inheritance of code, let’s define the method Generate(), which creates a code by name (as both class define various renderings, we have to define the class twice:
public class HtmlElementBlock : HtmlElement { public string Generate() { return string.Format(@"<{0}></{0}>", Name); } } public class HtmlElementNonblock : HtmlElement { public string Generate() { return string.Format(@"<{0} />", Name); } }
To test it, we will use demo application with a button and a textbox (multiline set, wordwrap banned and forced scrollbar - of course, this is not essential):
private void button1_Click(object sender, EventArgs e) { HtmlElementBlock div = new HtmlElementBlock(); div.Name = "div"; textBox1.Text += div.Generate() + Environment.NewLine; HtmlElementNonblock span = new HtmlElementNonblock(); span.Name = "span"; textBox1.Text += span.Generate() + Environment.NewLine; }
The result is obvious:
<div></div> <span />
And here we encounter the first disadvantage of our design. The way the object is used is in the full power of the programmer. If he defines "span" as a block element, the application doesn’t know it is a mistake. If we left the lower layer, we would have to define all HTML elements, but the programmer using our code would not make such error (intentional or unintentional). This is of course a matter of priorities and depends on the person who designs the solution.
Constructor and class Object
When we create an instance of the class, we write the magical word "new". What does it really mean? This is a special method that has static character (because we call it without the instance being created – see the chapter on calling members of the class, if it doesn’t exist). But where is this method?
public class Class { } // public class Class
Every C # class is automatically (unless otherwise defined) derived from the class "Object" or "object" (and as one of the classes has to be the "first," actually all classes have the class object at the lowest level). Even ordinal types like string, int .. are derived from this class – they behave differently from the object probably for historical reasons – they are not defined by a pointer, but refer directly to the value (see the chapter pointer on a pointer). Simply put, these classes are absolutely identical (it's just a different writing of the same output):
public class Class : object { }
The fact that we do not have to write the object is a simple property of compiler - it automatically adds it for us when translating.
Now we know how we can inherit different things between classes, but what is hidden for us in object class? Let's put the cursor on "object" and press F12, which will transfer us to a class definition (simplified):
public class Object { public Object(); public virtual bool Equals(object obj); public static bool Equals(object objA, object objB); public virtual int GetHashCode(); public Type GetType(); protected object MemberwiseClone(); public static bool ReferenceEquals(object objA, object objB); public virtual string ToString(); }
And here we find our hidden constructor. Although it has a different name, it's because the classes must have different names, and it is the name of the class that is reserved by the constructor. We do not affect the so called default constructor, but we can look at how the default constructor behaves in the following hierarchy:
public class Class { public Class() { MessageBox.Show("Class"); } } public class ClassB : Class { } public class ClassC : ClassB { public ClassC() { MessageBox.Show("ClassC"); } }
And the use:
ClassC class = new ClassC();
So, if we create similar constructors and subsequently inherit them, they are called automatically in hierarchical order: Object () Class () ClassC (). If we do not use a constructor in any intermediary that does not mean that we have banned it! This may be a little misleading as the mentioned property applies only to default constructors (those without parameters). If the inherited classes do not prescribe it, it simply expires and it is no longer possible to continue to call it from the upper classes, and vice versa, you cannot avoid the call of the default constructor. We might talk more about overloading constructors later, now it's not important at all - because the behaviour can be reminded by the compiler.
What are constructors good for? They can initialize complicated values, they can do some initialization routine and prescribe a kind of "interface" of a class. If we work in a team, we can make the constructor say, "Hey, look out for this!". In general, the constructor should not be difficult - the complexity attracts bugs and the exception in the constructor should not occur (we'll talk more about it in the chapter on memory leaks).
We should get some additional features into our HTML classes, with common HTML element like ID, CSS, so the programmer does not need to know (if there are too many) what is important for the class. Of course, the name is perhaps the most important, therefore, we place e.g. in the constructor:
public class HtmlElement { public string Name; public HtmlElement(string name) { Name = name; } }
If we try to use a new constructor in the application, it does not work there:
private void button1_Click(object sender, EventArgs e) { HtmlElementBlock div = new HtmlElementBlock("div"); textBox1.Text += div.Generate() + Environment.NewLine; HtmlElementNonblock span = new HtmlElementNonblock("span"); textBox1.Text += span.Generate() + Environment.NewLine; }
The message tells us that the constructor does not exist! And that's exactly what the previous paragraph was about. As soon as we add parameters of a constructor, it is no longer default, and thus does not inherit implicitly. Therefore, we must also define it in the upper classes.
If we add a constructor:
public HtmlElementBlock(string name) { Name = name; }
.. The class is untranslatable then, because the compiler found that the constructor already exists in the hierarchy. We need to 'pair' these two constructors. This is done by calling the original constructor call through the base keyword:
public HtmlElementBlock(string name) : base(name) { Name = name; }
Here we can use the definition parameters, or we can enforce them (e.g. 'div'), so we can call non-default constructor from default. We do not call the parameter:
HtmlElementBlock div = new HtmlElementBlock(); textBox1.Text += div.Generate() + Environment.NewLine;
... As it is enforced in class already:
public HtmlElementNonblock() : base("div") { }
We will not use this case this time, we will simply send the parameter to come to the ancestor. The question now is whether to use the constructor in the ancestor, or just define two constructors on the level of "performance" classes. Hard to say ... if we defined the parameter in the ancestor, then the setting probably should be there. This interface also suggests the nature of HTMLElement class - which enforces the name, other classes should not be bothered by that. So the resulting code might look like this:
public class HtmlElement { public string Name; public string Id; public string Css; public HtmlElement(string name) { Name = name; } } public class HtmlElementBlock : HtmlElement { public HtmlElementBlock(string name) : base(name) { } public string Generate() { return string.Format(@"<{0}></{0}>", Name); } } public class HtmlElementNonblock : HtmlElement { public HtmlElementNonblock(string name) : base(name) { } public string Generate() { return string.Format(@"<{0} />", Name); } }
And the use:
private void button1_Click(object sender, EventArgs e) { HtmlElementBlock div = new HtmlElementBlock("div"); textBox1.Text += div.Generate() + Environment.NewLine; HtmlElementNonblock span = new HtmlElementNonblock("span"); textBox1.Text += span.Generate() + Environment.NewLine; }