38
The Three Pillars of OOP (Object Oriented Programming, that is) by Michael F. Lynch PhD Version 1.0 – Released September 17, 2007 The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 1

The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Embed Size (px)

Citation preview

Page 1: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

TheThreePillarsof OOP(Object Oriented Programming, that is)

by Michael F. Lynch PhD

Version 1.0 – Released September 17, 2007

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 1

Page 2: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

IntroductionThis article is an attempt to introduce most of the concepts involved in Object Oriented Programming (OOP), an important aspect of building modern, scalable and maintainable. It does so by also introducing how OOP is done inside of C# and .NET.

OOP, by itself, is only part of the Object-Oriented picture; there are also Object Oriented Analysis (OOA) and Object Oriented Design (OOD). While this article is mostly about Object Oriented Programming, it's useful to look at OOA and OOD to see how it all fits together.

The use of the words Analysis and Design refer to various stages in the development of any large system, whether hardware, software, or both, and these terms have a long history in engineering practice. What follows here is a highly simplified view of what goes on during these stages of development.

During the Analysis Phase, analysts look at the problem to be solved in terms of the problem, seeking to understand the nature and complexity of the problem described using the established language and jargon from within the problem domain itself. During this phase, little thought should be given on specifying the nature of the solution; that is performed during the Design Phase. Instead, detailed conceptual views of the problem space are generated and, from that, a set of requirements for the system to be built is generated.

During the Design Phase, the designers and engineers begin to develop what will become the "solution", that is, the system to be built, which addresses the requirements uncovered during the earlier analysis of the problem. The solution generally proceeds from big-block top-level pictures down through ever-more detailed descriptions until actual implementation can begin.

The above description seems to imply that events proceed in a serial fashion from Analysis to Requirements to Top Level Design to Detailed Design to Implementation. In fact, at one time, it was believed by many (in particular the Department of Defense) that this is how software ought to be built. This was called the "Waterfall Mode", and these days it has been completely discredited.

What happens in the real world is that requirements, competitive pressures, technologies, personnel, etc. etc. are constantly shifting, and it is impossible to nail down anything long enough to be able to build an implementation that doesn't inevitably involve considerable rework in the face of ever-shifting requirements. This is the situation today, and new software development methodologies have been developed, for example, Agile and Scrum, which do a much better job of letting a team of developers build complex systems in today's dynamic environments and despite ever-changing requirements.

So where do OOA/OOD/OOP fit into this picture? They are, most of all, tools for

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 2

Page 3: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

helping to wrestle complex systems to the ground so that they can be clearly understood and developed. Without methodologies like Agile and Scrum and tools like OOA/OOD/OOP, building large systems becomes hopelessly complex and the chances for success quite risky.

Object Oriented Analysis (OOA), then, is part of the analysis phase in which we look at the problem space in terms of the problem space. If we were out to build some sort of medical information system, we would probably uncover, as part of our analysis, the notion of "patient". This leads quickly to the idea that there is going to be a need to retain certain pieces of information about the patient, like the patient's name and birth date. But at this stage we do not worry about how these are going to get represented in the final system, since that decision should be deferred until later. Say, however, that you were actually building a veterinary system. Now, besides name and birth date, we will likely also need to keep information about the patient, like its owner and what species it is. After all, caring for a gecko is probably going to be quite different than caring for a dolphin, and the subsequent analysis work will no doubt reveal substantial complexity in all the subtleties of caring for many different kinds of animals.

But what is uncovered during OOA are classes, which is the first notion we need to consider doing Object-Oriented development. During the analysis phase, when we specify classes, we seek to identify things from the problem space all the things we need to represent as classes. “Things”, here, can mean physical objects in the world (like "patients" or "engine blocks"), events (like the occasion of a patient visiting a doctor), or relationships (like a doctor-patient relationship, with its attendant details).

But during analysis, these are described in terms of the problem, so information about a patient or whatever is mostly descriptive, and not concerned with specific implementation details. As the process moves through design and on to implementation, these details get increasingly filled in.

During the design phase, the many classes identified during analysis are transformed into classes with far more rigorous specifications. For the most part, a class identified during analysis will go over more or less unchanged into the design. However, it often happens that a class identified during analysis needs to be defined better for design. Perhaps it is too big or too busy or made of of simpler parts that should in themselves be classes. In these cases, the class gets “unwound” or “decomposed” into more clearly defined and clearly refined classes.

Many tools have been developed over the years to make this process more rigorous and formal, not to mention less painful. Use Case Analysis and Use Case Designs, Unified Modeling Language (UML), Entity-Relationship Diagrams (ERDs), and so forth, are part of the arsenal of tools used to help understand and develop really complex systems.

When the design phase is complete, a large number of classes will have been identified,

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 3

Page 4: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

each with precisely specified data members and the operations (methods) the class needs to support to get its work done. When this stage is complete, programming can begin.

OOP – Time To ProgramOnce you get to the point where it's time to start implementation, most of the class definitions should already be firmly established as part of the outputs of the design phase. In each class, you know what data fields it contains, and what methods it supports.

Now it is time to look a little more closely at what a class is, and how we work with it.

Briefly, a class is a template for creating objects. What you write as a programmer when you design a class is a full class definition.

An object is an instance of a class. In most programs, you will often have many instances (objects) of a given class. There is a distinct difference between class and object, and it is a good idea to be rather precise in your use of the two terms. For a given class only one definition, that is, template, can exist. But there is no limit on the number of instances of a class that get instantiated (unless you deliberately restrict this).

Formally, a class is simply a user defined type (UDT) that is composed of data, along with functions that operate on the data. Here, “user” can refer to Microsoft, the author of the huge .NET FCL, .a third-party develop of classes (often called “components”) and even you, the programmer.

Designing a class is rather different than using the class in a program. There are two places where you do OOP:

● When you define the classes themselves.● When you create instances of a class.

Whatever classes you develop ultimately inherit from ancestor classes supplied to you by the development environment you are using. It is with these built-in objects, plus the ones you develop for your system, where the real work of your system gets done.

In .NET the “boss” object – from which all other objects are derived – is called System.Object. As the “boss” object, System.Object is, at the very top of the .NET class hierarchy. We will see more about this later on.

OOP – Designing ClassesIdeally, the OOD phase uncovered all the details of all the classes you will need to write. When it comes time to implement the system, the programming will consists of writing new classes as well as leveraging the thousands of available object classes rather that writing your own from scratch. The .NET Framework Class Library (FCL) has over 4000 classes ready for you to use. The Java development environment is similarly well equipped with

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 4

Page 5: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

thousands of classes. Note that you only will probably ever need to use a hundred or so (maybe 200), since most of the rest are highly specialized and the need for them rarely comes up.

If a built-in class does not do quite what you need, you usually create a descendant class from the built-in one that is closest to what you need. In fact, this is most often the most desirable approach, as we will see later. In the case of .NET, the classes we build will descend from more fundamental classes in the FCL. .. in fact you have no choice in the matter.

The Three Pillars of Object Oriented ProgrammingThe Three Pillars of Object Oriented Programming are:

● Encapsulation● Inheritance● Polymorphism

We begin by looking at Encapsulation.

Encapsulation – The First PillarVery early on, good programmers discovered that they tended to group data – and the functions that worked on that data – together, yet there was no “metaphor” for binding data elements to the functions that knew how to work on them. The Object Oriented Programming paradigm has been a 30+ year long effort to bring a formal model for data+code into being. Encapsulation is thus the resulting First Pillar of OOP.

The class brings together the data that belongs to that class along with the functions that know how to work on that data. In general, you want to hide the internals of your class as much as possible so that users of your class are protected from misusing it. Another important reason is so that the internal state of your object is protected from users, whether inadvertent or malicious! This is what encapsulation is all about.

So classes consist of two parts: data and methods. Whatever data can be stored in a class (it can be a little or a lot), the class's methods are the means by which users of the class can work with the data. This leads to our first pillar of OOP: Encapsulation.

The data in a class are variously called attributes, fields and properties.● Attributes usually refer to the class’s state information.● Fields are completely public data elements.● Properties are data elements whose access is controlled.

Functions that can work on data in a class are usually called methods. Certain kinds of functions are called events.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 5

Page 6: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Collectively both data and functions are called members.

In general, you have very fine-grained control over how the members in your class (both data and methods) are made visible to users of the class (i. e. object instances). We will see more about how this works later on.

With encapsulation, we can control read and write access to the member in several important ways. We can make a data member read-only, write-only (which is rare), or both. As part of controlling access to data members, we usually want to perform validity checks when users of class instances try to write a value to a member that’s been granted write access.

● Data members set up this way are usually called properties.● We use “gets” and “sets” to access their values.● Data members that are declared public (as we shall see) are usually called fields.

As a general principle, it's usually a better idea to arrange to have the data members in any class you design be properties rather than fields so that you can help ensure that only valid values are written. With publicly declared fields, you are at the mercy of users of your class (who are working with instances or objects) to write only valid values to the members. This is not to say that you should never have data members declared public; it depends on the circumstances and context, and you will need to rely on your experience and judgment to guide you. We will see a little more about this later on.

An Example in C#

Let's start with a simple example, a class for holding complex numbers. In C#, classes are declared with the keyword class, followed by its name, followed by some options, followed by a pair of curly braces. Here is the first pass at our complex number class.

class Complex { // functions and data members

}In Visual Studio, you can build a class any time you like by right mouse clicking on the name of your project in the Solution Explorer (the project name will be in boldface). Then from the pop-up menu, select Add New Item.... This will bring up a dialog box of “Visual Studio Installed Templates”. Select the one called Class and give it a good name, right then and there.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 6

Page 7: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Visual Studio will then create a new code module (i. e. a new file ending in .cs which has the same name as the name you just gave your new class). Notice the pattern here: a tendency to put each class in its own code module (its own file). This is an architectural model lifted straight from Java. If you are creating this class as part of building an ASP.NET web application you will get a message from Visual Studio asking if you would rather place the new code module in a sub-folder called App_Code. Generally you want to say Yes to this.

Everything else about the class is placed within a pair of curly braces, as shown. C# does not use header files, so everything about the class will appear here. Note that there is no semicolon after the name and the opening curly brace. Go straight to the curly braces and into the code.

Normally the class itself will be inside a namespace construct for sensible naming, but that is a discussion for later.

By default, your class inherits from System.Object. We will see more of this later on as well.

Now let's add some data elements to this class. Recall that a complex number has both a real part and an imaginary part. So far, so good, but the next question is, what sort of number should be stored for each? We could declare these as integer, but that would be overly restrictive, so perhaps a single- or double-precision number would be more appropriate. For maximal generality, using double-precision probably makes more sense than single-precision. Our class now looks like this.

class Complex { public double real; public double imag;

}At this point, the class is actually now usable, although it doesn't do too much. Note that both the real and imaginary parts are declared public. This is probably OK to do here, since if a number can be given a legal double-precision value for an ordinary real number, it is just as legal to do so in a complex number. Later we will see cases where declaring the data member public is probably a bad idea.

Constructors

So far we have been talking about designing classes. How do we actually use them in real

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 7

Page 8: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

code? Creating an instance of a class (that is, an object) is done with special methods called constructors. A constructor is what the user of the class calls to bring an instance of the class into being. Using our complex number example, we can create two instances of Complex like so:

public Complex R1 = new Complex();public Complex R2 = new Complex();

When this code is executed, two new complex number objects, R1 and R2 come into being. Note, however, that although these two objects come into existence, their real and imaginary parts remain undefined. Why is this? It's because we didn't explicitly design our class to deal with this situation, and leaving matters to defaults isn't always good practice. If we don't write a constructor for our class, a default constructor (one that takes no parameters) is assumed.

So let's do some more work on the Complex class.

First thing, is to add a constructor, one that takes no parameters, that simply sets the real and imaginary parts to zero.

class Complex { public double real; public double imag; public Complex ()

{real = 0.0;imag = 0.0;

} }Notice how the constructor is written. Its name must be the same name as the class itself... this is how the compiler knows that you mean to write a constructor. It is followed by an empty pair of parentheses, signaling that it is a method of some sort. Inside it (as defined by the curly braces), we have code that explicitly sets the real and imaginary parts to zero. Now we can get clean behavior from instances of our Complex class.

Multiple Constructors and Signatures

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 8

Page 9: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

We will see later that C# lets us write multiple constructors for classes we design. Most modern OO languages support this. This is called overloading., and it is very useful thing to be able to do in object-oriented programming.

You will often want to define multiple constructors for the classes that you develop. But each constructor must have its own unique “signature”. A signature refers to the unique and particular arrangement of parameters that is passed to a method. (This is actually the case for any method, not just constructors.) For example, these two methods have the same name, but different signatures:

public void WriteLine (int value); public void WriteLine (string value);

No two constructors (or any set of same-named methods, for that matter) for the same class can ever have the same signature. It's the structure of the signature, and the fact that they all differ, that allows the compiler to know which version of the method to apply when multiple versions are available. Inside the FCL, there are many, many places where multiple versions of a method are available. For example, the ToString() method for the DateTime class offers several variants to provide formatting options of how the date/time string is to appear.

Continuing our example, we can add two more constructors in the hope that we can make our class more useful. We will add two more: one which takes one double-precision argument, and one that takes two double-precision arguments, like so::

class Complex { public double real; public double imag; public Complex ()

{real = 0.0;imag = 0.0;

} public Complex (double re)

{real = re;imag = 0.0;

}The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 9

Page 10: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

public Complex (double re, double im){

real = re;imag = im;

}}

It should be clear what is going on. The one-argument version allows the user to specify the real part when constructing a new Complex object. The two-argument version allows the user to specify both parts when constructing a new Complex object.

public Complex R1 = new Complex(12.3);public Complex R2 = new Complex(12.3, 4.56);

This all works just fine, and gives the user some latitude when using the class.

Destructors

Many OO languages also support the idea of the Destructor, a method that cleanly destroys an object, that is, frees all its memory. You usually do not need to write destructor methods for your classes in the .NET framework. Since .NET garbage collects no-longer-used objects for you, however, there is usually no need for explicit destructor methods in the .NET framework. C# doesn't even have a special syntax for it.

However, C# does have a mechanism for speeding up destruction of unneeded objects, in those cases where you need to speed up their destruction. If so, your class should implement the IDisposable interface, which is discussed in greater detail later in this article. This is generally needed only when your class makes use of “precious system resources” that have to be returned well before the Garbage Collector gets around to it.

Overloading Methods

An overload is done to the same-named method when you want to provide several variations of the method all under the same name. Each version of a method must have its own unique signature. We already saw how to do this for Constructors, but it can be done with any method that it makes sense for. Hence, any method can potentially be overloaded.

Remember, the signature is the unique and particular arrangements of parameters passed to

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 10

Page 11: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

the method, and so each overloaded method you write must have a different signature from all the others. An example will help make this clear. Assume we have…

n a class called Employees, which is a…n collection of instances of another class called Employee.

We want to be able to look up an employee’s start date by either their EmployeeID or their SSN:

DateTime started;started = theEmployees.GetStartDate (1234);started = theEmployees.GetStartDate ("123-45-6789");

In the above example, it is OK to have two methods for Employees both having the name GetStartDate, since one version expects an integer and the other expects a string. The compiler can always tell them apart.

Visibility of Class Members

As noted earlier, you have very fine-grained control over how the members in your class (both data and methods) are made visible to users of the class (object instances). We look at this in more detail now.

Visibility is something we can control for both data members (the “data”) and the functions (the “methods”), so much of this discussion applies to either.

Easiest is simply to declare the member public, as it is both for the class itself (Employee) and its data member EmployeeID.

public class Employee{

public int EmployeeID;}

A data member declared this way is sometimes called a field. It can both be read and set without limitation (except that enforced by the underlying type) by users. In general, simply declaring a data member public is often unwise, since you then have no control over what values users put into it.

C# offers the following five levels of visibility, from most to least visible, to control access

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 11

Page 12: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

to both data and methods:

● public ● internal● protected ● protected internal● private

These five levels have the following meanings:

● public members are visible from any place else in the program, even from other code modules (assemblies) that create instances of the class.

● internal members are only visible from inside the same code module. But within that same code module, one object class declared there can access internal members of other classes declared there.

● protected members are visible only from within the class to which it belongs or from within any descendant of the class. The descendant class code can be in another module (and in fact probably will be). You will use protected-level a lot when you design carefully thought-out class hierarchies.

● protected internal members can be accessed from within the current assembly or from within classes that descend from the current class. It is basically a union of the protected and internal modifiers.

● private members are visible only from within the class itself. That’s it; no other class in any other module can access private members, not even classes that descend from this class.

Read and Write Access

In addition to controlling overall visibility, you can additionally make data members read-only, write-only (very rare), or both. You can also use this capability to perform validity checks or take other actions when users attempt to write a value into a member for which you supply write access. Data members set up this way are called properties. We then supply “gets” and “sets” to access their values.

● Write a get method to allow a user to read a member value.● Write a set method to allow a user to set a member value.

In your set method, you have an opportunity to perform appropriate validity checking before actually changing the data member's value. Refuse to follow through on setting a value if you are handed bogus data.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 12

Page 13: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

To create a read-only property, omit the set method. There will then be no way for users of your class to change that data value. To create a write-only property, omit the get method.

Write-only is generally poor practice, since the behavior that results can be confusing for users of the class. About the only legitimate use for write-only is for setting secret encryption keys. Once the class itself (more on this later) sets the secret key no one using the class can see what it was. If you must have a write-only property, it is usually better to write a completely separate method (one that is not part of the get/set pair) to deal with these write-only cases. This minimizes confusion among users of your class, who would otherwise see the write-only property as something of a mysterious “black hole”.

You often want to write your get or set methods to perform other actions that make sense inside your class (like ensuring internal consistency or causing an intentional side effect). get usually simply returns the property value, but you could, for instance, also log the access somewhere. set is best used for performing appropriate tests on the passed-in new value before changing the value. You may also want to have certain side effects take place inside the object whenever a value is changed using set. Writing a log entry to a log file might be one of those side effects.

What does this look like in C#? Say that somewhere inside of a class is a declaration for a variable named itemQuantity whose access we want to tightly control. The code could look something like this:

private string itemQuantity; // lower case public string ItemQuantity // upper case{

get {

return itemQuantity; // returns the private value} set {

// value is built-in and always of correct type// only change itemQuantity if the test returns True if ValidateItemQuantity (value) {

itemQuantity = value; }

} }

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 13

Page 14: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Here we allow ready retrieval of itemQuantity, but allow its value to be changed only if it passes muster in a call to ValidateItemQuantity(). Only if its value is OK do we actually commit the change.

Note the .NET naming conventions found in the above example. The data member itself is declared private within the class and its name begins with a lower case letter. The corresponding property is declared public and its name is the same as the private data member, except that it begins with the upper case letter.

This is considered standard practice (since C# is case-sensitive) and makes for very clear code. Also note that:

● get and set appear as shown, in lower case, and without parentheses.● The get and set appear immediately after where the property is defined.● Unlike the C++ convention, you do not supply a passed-in parameter for the new

value in the set method.● The compiler implicitly assumes exactly one passed-in parameter for the set

method, whose name is always value, which is automatically and always the same type as the underlying property.

An unfortunate design decision in C# is that you end up with the same access permission for both get and set, since only the data member itself that has the access modifier attached. In other words, you cannot declare the get to be public and declare the set is to be protected, for example. Yet you may in some cases want to restrict the ability to set a value to derived classes only (i. e. protected), while allowing public read access. The only way to do this is with a workaround: Write a completely separate method (marked protected, or whatever), and perform the value-setting code in there, and omit the usual set method. Then, only derived classes will be able to do set calls to the property.

Finally, note that C# handles this sort of thing differently from C++. The older convention from C++ looks like this. For a member named MyField, write:

private int MyField;public fieldtype GetMyField();public void SetMyField (value fieldtype);

(The actual code isn't shown.) Here, GetMyField() need only return the value of MyField, but SetMyField() can and should ensure that the value passed in makes sense before actually changing the value of MyField.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 14

Page 15: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Static Members

Static members are ones that exist only at the level of the class. These are declared with the keyword static.

In the case of data members, only one such “instance” exists, and it belongs to the class itself. Static data belongs to the class, not to any one object. No matter how many or few instances of the class are created, only the one static instance exists, and its value is (potentially) available to all object instances (provided that it was also not declared private).

Many times, when you do permit access to the value of a static data member, you want to grant only read-access and strictly control write-access, which is why you are probably using static in the first place. In such cases, you might provide for a separate method that controls writing to the static data member, and that method may be declared protected or given some other restricted status, or it may impose other restrictions on its use.

static has uses in constructing things like the design pattern called the Singleton, the correct way to implement “global” variables in those situations where you really think you need them. Static data members are also often used inside of the class constructors and a few other places you control.

In the case of static methods, the method, too, belongs to the class and not to any particular instance of the class. Recall that the method called Main(), which is where execution begins in a C# program, had to be declared public void static or public int static, since execution had to begin somewhere, before any instances of any classes are brought into existence.

In the example given earlier, for the write-only encryption key, you would probably want a static method in the class itself that took care of setting the secret encryption key. By also declaring it private, you could ensure that no other caller could ever access this critical piece of code, and that, once set, it could not be hacked or changed. (Of course, there are often ways for a clever hacker to get around this, but this isn't a course about computer security, which is a vast topic in its own right.)

Exploring the Framework Class Library (FCL)

The .NET Framework Class Library is the foundation of all .NET programming. The FCL contains thousands of useful classes. The underlying framework of the FCL also permits

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 15

Page 16: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

third-party vendors to develop additional class libraries for particular problem domains. As developer, you also get to extend the FCL with classes of your own design.

All .NET- aware languages, and C# in particular, are necessarily deeply intertwined with the FCL. As we have seen, the syntax of C# itself is not much different from other languages which which you may already be familiar. Proficiency with .NET is largely a matter of knowing something about the full range of the classes available to you. In practice, however, you really only need to know about a couple of hundred classes to be able to do useful work in .NET. Most of the other classes are highly specialized and are needed very rarely, if at all.

All Objects in .NET descend from System.Object, which is the master class for all Objects both in the Framework Class Library and for any classes you develop. System.Object defines a number of core methods that are useful everywhere throughout .NET and in all children classes.

In C# code, you can use the keyword object (lower case) as a shorthand for System.Object. We will look more closely at the inner working of System.Object during class time.

A Few Handy Classes

Remember that everything in .NET, except for the fundamental types like integers and floating point values, are objects.

In particular, everything we use to create visual elements, whether in Windows applications or Web applications, are built up from a wide variety of objects. Much of the FCL consists of a powerful set of visual objects, an entire set for building standalone Window applications and another set for building Web applications.

Visual objects are objects that know how to draw themselves and respond to events. They are a big part of building graphical user interfaces (GUIs). Getting comfortable with the large set of visual objects is part of the essential skills in building these sorts of applications.

In the early labs, there were a number of classes that did most of the hard work.

n DateTime – knows all about managing dates and timesn TimeSpan – knows how to deal with intervals of timen Convert – a smart way to convert data types

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 16

Page 17: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

n Math – where Sin() and π (Math.PI) liven Session – essential for keeping track of web clientsn Response – used to jump from one web page to another

The Console Class

The Console class is provided in .NET for building simple console applications (which run in MS-DOS style terminal windows). Console applications are surprisingly useful for:

● Building and testing simple classes of your own,● Building broad classes of utility programs, essentially for doing the same sorts of

things as PERL does.

The main methods we are interested in for the Console class are:

● Read()● ReadLine()● Write()● WriteLine()

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 17

Page 18: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Inheritance - The Second PillarAs we have seen, the Three Pillars of Object Oriented Programming are:

● Encapsulation● Inheritance● Polymorphism

In this section we look at Inheritance.

Inheritance is the Second Pillar of OOP. Essentially, inheritance is the ability of the object-oriented framework in a language to let you design new class definitions based on existing class definitions. Inheritance is closely related to polymorphism, the Third Pillar of OOP.

The term parent or ancestor class refers to the class from which we derive. The term child or descendant class refers to the new class we are designing from the parent.

Broadly speaking, inheritance comes in two flavors:

● Single Inheritance – the child class can derive from only one parent class.● Multiple Inheritance – the child class can derive from several parent classes.

Many OO experts now regard multiple inheritance as a bad idea, although experts do not agree on this point and some think multiple inheritance is just peachy. For those who do not like multiple inheritance, however, there is a related capability called the Interface which can provide many of the same capabilities. Note that this use of the word “interface” has quite a different meaning than it does in, say, User Interface.

Multiple Inheritance

As OO evolved, it became evident – at least to some experts – that multiple inheritance was a mistake, and so newer and better ways to handle the alleged need to have multiple parent classes were devised:

● Ordinary “has-a” relationships● Containment-Delegation● Interfaces

Pure inheritance itself reflects the “is-a” relationship.

Is-A and Has-A

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 18

Page 19: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Conceptually, these two epistemological ideas go to the heart of Inheritance.

● A car is-a vehicle● A car has-a radio

It is perfectly OK for one class to contain references to other classes, or even references to the same class… this is useful for things like trees and linked-list objects.

Containment-Delegation

In a Containment-Delegation relationship, one class “has-a” another class. The latter class (which we can call the “inner” class) is contained by the owning class (the “outer” class). In the above example, the car has-a radio.

The outer class can then make as much or as little of the functionality of the inner class available as it likes, by supplying methods of its own that simply call methods of the inner class (this is delegation).

This allows users of the outer class to get at carefully-selected functionality of the inner class, as exposed by the outer class.

Consider the car has-a radio scenario:● The car class can offer to outside callers services like TurnRadioOn(),

TurnRadioOff(), SetRadioVolume(), etc.● These methods in turn call methods that belong to the actual radio object.● The caller of the “outer” methods is unaware that delegation has taken place inside

them. It's none of the caller's business.● The inner workings of radio are kept hidden from the user of the methods

offered by the outer class.

The Interface

This is the third way of avoiding multiple inheritance. We will return to it after we see how multiple inheritance works in C#. How Inheritance Is Done in C#

If your class inherits from another, follow the class name with a colon, followed by the class it inherits from:

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 19

Page 20: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

class MyNewClass : OrgBaseClass { // same as before }

Recall that .NET is based on the single inheritance model, so only one ancestor class (the one after the colon) can be specified.

When you create a class that inherits from another class (its parent or ancestor), everything from the parent or ancestor class is inherited. This is a powerful way to extend the capabilities of existing classes by letting you customize and extend their behaviors in ways more appropriate for the problems you want to solve.

Adding Interfaces

Since .NET is based on the single inheritance model, you can have only one ancestor class, but you can add as many Interface definitions as you require. These are simply listed after the base class, separated by commas. Here's an example:

class MyNewClass : OrgBaseClass, IDisposable, ICloneable{ // same as before}

In this example, MyNewClass descends from OrgBaseClass, and also agrees to support the IDisposable and ICloneable interfaces.

More about Interfaces

We have mentioned the Interface as a way of avoiding multiple inheritance without exactly explaining what an Interface is.

In essence, an Interface represents a different form of a has-a relationship, but not in the same sense as object A “has-a” reference to object B. Rather is is more like saying that an object A “has-a” particular capability. For example, you may want a class to be able to print out its values to a printer. In that case, you may want to have an interface called IPrintable, which provides for this capability for printing.

An Interface is then essentially a contract, an agreement to support whatever actions the Interface defines. When you add an Interface to your class definition, you are essentially

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 20

Page 21: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

promising that you will supply working code for every method defined in that Interface. If you don’t, users of your class who expect the promised functionality will see things break. This is not good.

The purpose of this contract is to permit users of the class to be able to rely on a stable, clear and consistent definition. This is how it is possible for a user of an object class to know in advance that, if an Interface is supported by a class, the class can be relied upon to do the right thing. With Interfaces, even classes that are quite dissimilar may still implement the same Interface for some reason. You access the methods of the Interface in one class the same as you would the methods of the Interface the other class, and can even put in cast operators to make that happen. With class hierarchies this would usually be a mistake, but it makes sense for Interfaces.

Note that Interfaces are not classes, only a kind of chunk of code that can be made part of classes. By convention, the name of any Interface starts with capital-I.

An Interface is a template that defines some number of methods that make up the interface, i. e. the names and signatures of those methods. The Interface definition itself does not, and cannot, contain any code. You must supply the code for the Interface if you choose to support the Interface in any of your class definitions. That’s part of your contractual obligations you took on by including the Interface.

.NET defines a number of useful Interfaces that you can use in your own code. One of these is called IDisposable, and it is used for those situations where you want to take control of when garbage collection occurs for instances of your class that are no longer needed. IDisposable defines exactly one method called Dispose(), which, when called, destroys whatever data your class defines, as well as perform any other clean-up actions that your class might require. If you include it in your class declaration (as in the above example) then you must write code for its corresponding method Dispose(). IDisposable is a simple Interface; others may require rather more work from you.

The act of supporting an Interface is called implementing the interface. In summary, if you implement an Interface, you must then ...

● supply all the necessary code...● using the exact names and signatures as defined in the Interface itself…● but some of these can be stubs (i. e. contain no code), if that makes sense within the

context of how you are using the Interface.

Restrictions On The Interface

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 21

Page 22: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Interfaces cannot contain any code. This means they cannot include gets or sets, either.

Interfaces do contain declarations of methods, properties, indexers, and events, but not fields. The reason Interfaces cannot contain fields – it would imply a declaration of some variable within the Interface, yet you cannot have any code there.

You do not ever instantiate an Interface they way you would a class. In other words, you cannot bring an instance of just an Interface into being. Instead you include the name of the Interface in your class definition and then fill in all the code that the Interface requires you to supply when you write the code for the class.

In C#, you indicate the Interfaces you intend to support by listing them in your class definition following the colon and the ancestor class (if any). If you support multiple Interfaces, list them, separated with commas (see the example for MyNewClass above).

Note that a derivation from an Interfaces acts completely independently from a derivation from a base class. For example:

class MyNewClass : IDisposableor

class MyNewClass : BaseClass, IDisposableor class MyNewClass : BaseClass, IDisposable, ICloneable

An Interface cannot define any constructors, because it is not a class. Instead, the code for the Interface methods that you write simply becomes available to instances of your object. Recall that you contracted to support the Interface.

No operator overloads are permitted, so that languages that don't support operator overloading can still support Interfaces.

Inside the Interface definition itself, all Interface members must be public. They have to be, otherwise there would be no way to expose them to view.

There cannot be any virtual methods inside an Interface, since no polymorphism is possible with an Interface. There definitely cannot be any static methods, since there is no code allowed in the Interface definition, and if a method were static, you would have to supply code. If you really need virtual or static methods, provide for them in the implementation of the Interface; tt just cannot be imposed by the Interface definition

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 22

Page 23: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

beforehand.

Inside your class, you must write code for all the methods called for in the Interface(s) you chose to support. These methods must be written with the exact signatures previously defined for them by the Interface. You will get compilation errors if you leave any methods out, but it is OK to write a method that "does nothing" (called a stub) if that is the most sensible thing for your class to do with this Interface.

The IDisposable Interface

A sometimes useful interface in .NET is IDisposable, mentioned earlier. It defines a single method, Dispose().

public interface IDisposable {

void Dispose();}

You will usually want to support this Interface for a class that you develop if that class is making use of some resource whose lifetime must be controlled. These are often “legacy” components like COM, Active-X, etc., which predate the .NET framework, do not participate in .NET garbage collection, and which require explicit memory management on the part of the programmer.

Other places where this may be appropriate are for external resources, for example a log file that the class has to write to. You want to be sure to close any file handles before letting your class be garbage collected. Also, things like network socket connections or other operating system resources. O/S resources should always be considered “scarce”, and you generally want to free them as quickly as possible when you are through using them.

Declaring Your Own Interface

You can create your own Interfaces for use in other classes that you want to write. Assume for example that you want to create an Interface called IBankAccount to represent money for a department in an ordinary business. You want it to represent a money amount, as well as the notions of depositing and withdrawing money (that is, define methods for depositing and withdrawing money).

You could then use this Interface to represent movement of money between departments (similar), or between a department and the bank holding the account (dissimilar). You

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 23

Page 24: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

would of course then need to deal with the fact that a deposit to one is a withdrawal from another. The beginnings of a declaration for this Interface then could look like this, in C#. (Of course, a real system would probably have more complexity in here than this.)

namespace MyCompany.MyBigApp.MainProject{

public interface IBankAccount { void Deposit(decimal amount); bool Withdraw(decimal amount); decimal Balance { get; } }

}

A Canonical Inheritance Tree

Let's get back to that Second Pillar, Inheritance. We have just seen some ways, notably Interfaces, which let us avoid things like multiple inheritance, but now lets look at what inheritance is all about. Say we want to build some sort of “paint” program, and as part of doing this, we want to let users draw various shapes on the screen. This suggests an opportunity to construct an inheritance tree to represent these shapes.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 24

Page 25: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Here, Shape is going to be a parent class for all the specific shapes we want our paint program to support. Why do we want to do this? Well, it is likely (in fact, probably inevitable) that all shapes will share some common items of data, like height, width, position and color. These are best “factored out” into the Shape class, from which each particular shape sub-class will inherit. But how best to handle the Shape class?

Abstract Classes

An abstract class (declared with the keyword abstract) is a class in which it is illegal to create instances of the class. Why would we do this? In the above example, the Shape object is something we would not want allow anyone to create directly (it has no actual “shape”!). Instead, we want users to create specific shapes by using the many specialty shape sub-classes that we supply.

Nevertheless, in the Shape class, we will define a number of members – both data and methods – common to all shapes that derive from this class. Some of these could easily be height, width, top, left, and color. These are all data elements that probably make sense to include in a Shape object, as well as an abstract or virtual method for actually drawing the shape, Draw().

Because the parent class is abstract, all child classes must supply actual working code for all the methods. After all, hexagons are drawn differently from ellipses.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 25

Object

Shape

Ellipse

Triangle

Rectangle

Hexagon

We would probably define this as an

abstract class

Polymorphism

Object

Shape

Ellipse

Triangle

Rectangle

Hexagon

We would probably define this as an

abstract class

Polymorphism

Page 26: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Note, however, that there likely going to be some common set-up code that we want in Shape.Draw(), which is the method found in the (abstract) parent class. This could be set up code, for example, for preparing a “drawing context” that gets things ready to actually draw the object. If that is the case, then it would certainly be convenient to do this set up in a centralized location, and that location is most sensibly Shape.Draw().

If so, then we want to declare the Shape.Draw() method virtual, not abstract. This means that this method will in fact contain callable code, which is exactly what we want. Note that the Shape class itself can still be declared abstract, with a method declared virtual, and that is perfectly all right.

Why Do Abstract Classes?

We make use of abstract classes mostly to produce better designs! Think of it as an opportunity to “draw a line” in your design and define a class for which you intend to derive many useful subclasses.

You “factor out” as many common data members as you can and put them in the abstract class. You also “factor out” as much useful functionality as you can and put it in as method definitions in the abstract class. This was the whole point of having an abstract class in the above example, from which as many different shapes (sub-classes) as we desired could be derived.

As we saw, not every member in an abstract class needs to be abstract. But if even one is, the whole class must be regarded as abstract, and instances of the class cannot be created. This makes sense, because with even only one member declared as abstract, there would then be no way to create an instance of it.

How C# Does It

In .NET, everything somehow inherits from a built-in class, unless it is a simple value type, like integer or Boolean. By default, every class inherits – at the very least – from System.Object, unless it inherits from something more appropriate. As we have seen, namespaces are used to help keep it all organized.

Recall that .NET is based on the single inheritance model, so only one ancestor class can be specified. So only simple Inheritance from another class is possible. This is how we specify the inheritance.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 26

Page 27: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

As we have seen, though, you can include as many Interface definitions as you require. These are simply listed after the base class, separated by commas.

Once you have created a class which inherits from some parent class, what happens next?Time to add whatever data members and methods you need to get the unique new functionality you want your class to have. As always, you have very fine grained control over how this is done.

The first concern is whether our class needs to be made abstract or not. This will usually be useful when you intend to create two or more different subclasses, an idea we will return to in the Third Pillar: Polymorphism. If you make your class abstract (or if even one data member or method is declared abstract), then you will of course need to supply some subclasses, or else require users of your class to create their own subclasses. Remember, no instances of an abstract class can ever be created.

The abstract Keyword

Having a property declared abstract declares the type of the property (and whether it has get or set or both) but supplies no actual code for getting or setting. Derived classes must supply the get and set code. A method declared abstract in the parent class definition implies that its child classes must implement the same-named method, since there is no code whatsoever in the abstract class itself.

The virtual Keyword

A method declared virtual in the parent class definition indicates that it can be overridden in child classes. The child class can then override the method, using its own code, with the override keyword. Overriding in the child class is always optional, since the base code is still available.

Virtual members include both properties and methods (but usually not fields). You don’t have to override the code of a virtual method, but in most cases you will want to. If you don’t supply an override method in the child class for a virtual method, the system simply uses the code in the parent class. Sometimes this is exactly what you want.

If you don’t include the override keyword for a method in your derived class, you are effectively hiding the method in the parent class. This is usually a mistake. If you do want to hide the parent method, the keyword new should be used in the method

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 27

Page 28: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

declaration to indicate you want this behavior; otherwise you get a compiler warning from Visual Studio.

When you do override a virtual method in the ordinary way, you may often still want to call the original code in the parent class (because, for example, it does something useful common to all subclasses). This could be the case in our Shape example, where a method called Shape.Draw() does some useful set-ups, perhaps like retrieving a “drawing context” for the graphics system you happen to be using, and you need to do this for whatever hexagon, ellipse, etc. the user happens to be drawing.

To do this, add the base keyword as shown when you need to call the original parent method:

base.<MethodName>In the Shape example in, say, the Hexagon's Draw() method, you could call the Shape's Draw() method:

class Hexagon : Shape{

...public void Draw (){

base.Draw (); // Calling Shape's Draw method// Draw the actual hexagon...

}...

}

Overloading and Overriding Methods

Don’t confuse override with overloading. override means you intend to supply a method in a descendant class that is to be called instead of the same-named method from the parent class that it descends from. Overloading means to declare multiple versions of a method with the same name but with different arrangements of parameters (i. e. having different signatures). For overloading, all versions of the method are named identically; only their signatures are different. The C# compiler is smart enough to detect when you are overloading a method. However, some languages do supply an overload keyword to signal the desire to set up overloaded methods.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 28

Page 29: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

The sealed Keyword

Use the sealed keyword on a method to prevent it from being overridden by any descendant class. When used with a method, that method overrides an inherited virtual method, but no classes that inherit from this class can override it any further. sealed must be used in conjunction with override.

sealed can be applied to an entire class, in which case users of the class must create instances of that class itself; they are not going to be able to create subclasses from it. String is a notable instance of a sealed class.

A Warning and a COM Sidebar

Microsoft has an older technology called the COM object that has been a mainstay in Windows programming for years. COM objects are the basis for so-called VBX and OBX visual objects, the very large set of Active-X objects, and extended objects called COM+ and DCOM objects.

All these COM objects (now considered legacy) define an elaborate Interface mechanism that has nothing to do with the Interface mechanism for .NET that we have been discussing here. The .NET Interface mechanism is entirely unrelated to the COM version, but is modeled instead after Java.

In fact, if you want to use COM objects in what is otherwise a .NET application, you have to manage their usage. Since they are not .NET classes, the Garbage Collector cannot work with them. If you have occasion to work with COM objects of any sort, be careful not to get confused by its use of Interface versus the meaning it has in .NET

The interface mechanism for COM objects begins with the IUnknown interface. IUnknown is the simplest COM interface, and all COM objects must support it. It is a way for a caller to ask the COM object what it knows how to do and what other, more detailed, interfaces it supports.

From there a caller of a COM object can find out what other interfaces that COM objects supports, and if that matches the caller's expectations, the caller can now work with the COM object via the interface it knows how to use.

This mechanism was actually quite new and useful for its day (plus, it “worked”) but it had a few nasty properties that could trip up the unwary programmer. For one thing, it had a

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 29

Page 30: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

primitive notion of garbage collection of its own: every time a programmer wanted to use a COM object the programmer had to arrange for the program to “register” its usage; then, when the program was finished with the COM object, the programmer had to arrange for the program to “unregister” it. If all went well, these would be in balance, and the run-time code that managed COM objects would be able to get rid of ones that were no longer in use. However, it required that programmers really register and unregister COM component cleanly; if a program ever “forgot” to unregister a component that it had registered, or, worse, if the program crashed before it could unregister the component, then the component would never be released from system memory and this would eventually impair the operation of, or even crash, the operating system itself. This was one cause for Blue Screens of Death (BSODs).

To note again: This is not the mechanism found in .NET for component management or garbage collection.

More About Constructors

In .NET there exists the Object() constructor, which is ultimately the boss constructor for every object that gets built in .NET. As we have already seen, the constructor is how we create new objects at run-time. You may sometimes see this abbreviated to “ctor” in some discussions about .NET or other discussions about object-oriented programming.

Eventually any constructor for any descendant class, either built-on ones or ones you design, calls this method to set up the basic info common to all objects. One reason this has to happen is to let the .NET run-time know about all the objects that are getting created so that garbage collection can operate correctly with them. Recall that objects in .NET are created on the managed heap. The System.Object class knows all about how to work within this environment, and so your classes do as well.

There is more going on with constructors than meets the eye. There are actually three types of constructors: instance, private, and static. The one we're most familiar with is the instance constructor – it's the kind that is invoked when you use the new keyword.

As we have seen, if you don’t supply a constructor for a class you design, a default one is provided. Default constructors take no parameters.

But most constructors can be overloaded, and, in many cases, they should be. Providing an appropriate set of constructors is an important part of designing a class well.

Constructors can also “chain”, which means that one constructor can call another constructor, which can call another constructor, and so on. Sometimes a constructor calls

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 30

Page 31: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

another constructor from within the same class; other time a constructor can call a constructor from a parent class. More on all this below. Instance Constructors

These are the kinds to use when creating objects, which are instances of classes. As noted above, this is the kind of constructor invoked when you use the new keyword.

The layout for this kind of constructor is:

[attributes] [modifiers] identifier ([formal-parameter-list]) [initializer] { constructor-body }

[attributes] are used occasionally make use of various kinds of preprocessing directives (we haven’t talked about this in class yet). For more about attributes, see Conditional directive and Obsolete Directive in dynamic help.

[modifiers] are, optionally, the keyword extern and one of the four standard modifiers:

● public● protected● internal● private

extern is used to import methods from other modules or DLLs. See dynamic help for details.

identifier is the name of the constructor, which of course is identical with the name of the class itself. This is followed by formal-parameter-list, the list of parameters the routine takes. The particular pattern of the parameter types constitutes, of course, its signature. You can have multiple constructors (or indeed multiples of any method) as long as each one has its own unique signature.

Here is an example of a simple class (with inheritance) that has two constructors, both declared public.

public class MyChildClass : MyParentClass {

public MyChildClass() {

// constructor code goes here: do stuff

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 31

Page 32: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

}

public MyChildClass(int ID, string name) {

// do more stuff }

}

The initializer is patterned after C++ and is a great feature of C#. It tells the compiler that, just before calling this constructor, it must call the initializer constructor first.

Initializers can be either base or this, followed by a list of arguments:● base (arg-list) means to call the constructor having that arg-list signature in

the parent class.● this (arg-list) means to call the some other constructor having that arg-list

signature in this class.

Note that, since many of your classes will descend from existing classes, you will often want to first do all the standard set ups the parent class constructor provides. The base initializer lets you do this easily. If constructors in one class can reliably call the base initializer of its parent class, and so on, then initializing can “chain” all the way up to System.Object.

Similarly, if one of your constructors does a lot of work doing set ups, then other overloaded constructors in your class can invoke this heavy-duty constructor by calling it with the this initializer. If that heavy-duty constructor itself uses the base initializer, then a lot of work can be done easily and correctly, with all the set-ups begin done in the places where they belong. Learning how to design these sorts of constructors and constructor chains is an important part of doing quality OOP.

Here is an example:

public class MyChildClass : MyParentClass {

// This constructor calls the parent's constructor public MyChildClass() : base () {

// constructor code goes here: do stuff }

// This constructor calls this class's constructor ,

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 32

Page 33: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

// the one just above.public MyChildClass(int ID, string name) : this () {

// do more stuff theID = ID;theName = name; // etc.

} }

A Caution About Constructors

No matter how you set up the constructors, one point is key: Never put code that could potentially fail into a constructor. Example: code that depends on being able to acquire a system resource… the resource might not be available at that moment.

Instead, supply another method (often called Initialize) to handle this. You can set this up so that it can return a Boolean for success or failure. Then use that fact when doing initialization to signal abnormal situations.

Weird Constructors

The last two constructors we are going to look at are a little odd, but definitely have their uses.

Private constructors can be used for the Design Pattern called the Singleton, the correct way to have “global variables” if you must have them.

Static constructors can be useful for making objects database aware. The Employees class could use a static constructor to prepare itself so that it knows how to create “the next new employee ID” by connecting to a database and finding the largest ID number of any existing employee. More on this much later in the course.

Private Constructors

Any class containing a constructor marked private prohibits instances to be created.Such constructors cannot be called directly (since they are private). Why would we ever want to have this?

Private constructors are often used with static classes, which, in effect, “already exist” when referenced. Therefore they are good for managing things like global constants, math libraries, incrementing counters, etc. While they do not come up every day, when you need

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 33

Page 34: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

this particular behavior, nothing else will quite do. See the .NET on-line help for more info.

Static Constructors

Static constructors are a way to guarantee that certain things in the class itself get initialized before any instances of the class are created.

Static constructors cannot be accessed. Static constructors cannot take arguments. You cannot control when it gets called, but you can rely on it getting called before any instances of the class are created.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 34

Page 35: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Polymorphism – The Third Pillar of OOPLet's return to the earlier Shape diagram, which illustrates an example of polymorphism.

In our design, we can assume that each shape has its own idea of what it is and how it is to be drawn, yet they are all shapes. The Shape object is probably best made abstract, although it doesn’t have to be. We would now like each of our polymorphic classes to know how to draw itself.

In the above, the Shape class defines a method Draw()which is going to be declared either abstract or virtual. Each subclass will override the definition of this method and provide the code that makes that particular class draw itself correctly. If the parent class method is declared abstract, then of course the child class must provide an implementation.

Virtual Members

Virtual members (both data and methods) are part of the third pillar of OOP, polymorphism. A method declared abstract in the parent class definition indicates that it must have the same-named method supplied in child classes. A method declared virtual in the parent class definition indicates that it may be overridden in child classes, because there is already some code in the virtual method that is probably useful for a subclass to access. The child class can then override the method, using its own code, with the override keyword, which is always optional but usually desirable. The set-up we

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 35

Object

Shape

Ellipse

Triangle

Rectangle

Hexagon

An abstract class

Polymorphism

Object

Shape

Ellipse

Triangle

Rectangle

Hexagon

An abstract class

Polymorphism

Page 36: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

have in our paint program looks something like this:

Virtual members can include both properties and methods (but not fields, except as a hack). Static methods and properties cannot be declared virtual; it’s meaningless!

You are not required to override the code of a virtual method, although in most cases you will want to. If you don’t write an override method in the child class for a virtual method, the system will simply use the code in the parent class.

If you don’t include the override keyword for a method in your derived class, you will effectively hide the method in the parent class. You will rarely want to do this. If you do, the keyword new should be used in the method declaration to indicate you want this behavior, otherwise you get a compiler warning.

When you do override a virtual method in the ordinary way, you may still want to call the original code in the parent class (because for example it does something useful that is common to all subclasses).

Use this construction to call the original method in the parent class:

base.<MethodName>

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 36

Object

Shape

Ellipse

Triangle

Rectangle

Hexagon

public void Draw()

Draw()

Draw()

Draw()

Draw()

Object

Shape

Ellipse

Triangle

Rectangle

Hexagon

public void Draw()

Draw()

Draw()

Draw()

Draw()

Page 37: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

Why Do All This?

The beauty of this has to do with working with large collections of similar – but not the same – objects, for example, a draw program as we have here. In that example, note that the array contains elements declared as type Shape, not any particular shape, since we don’t know what shapes our users will actually use.

Later on, each of the Shape objects (of whatever subtype they actually are) will get drawn using that particular shape’s own Draw() method, without any additional code needed on our part. The correct overriding method is instead determined at runtime.

Going in the other direction is problematical, however. The declaration in the array was for the class at the topmost level (Shape) that made the most sense to use. No need to go all the way up to System.Object. Then, we used objects from various child classes for the actual objects in the array.

When working with an array, foreach .. in is handy for accessing the individual elements. The code for drawing the entire picture now becomes very simple indeed:

Shapes[] shapes = new Shape [MAX_SHAPES];

// ... time passes, now draw the shapes foreach (Shape nextShape in shapes) {

nextShape.Draw(); }

On the other hand, had we needed to “cross” classes we would need to apply a cast from one class to the other. If your design seems to need this sort of behavior, rethink your work, since it may be that you are doing something seriously wrong.

Conclusions

Getting good at OOP is a matter of understanding what the three pillars are all about and what implications they have on how your (and other’s) classes are designed and used. Object-Oriented Programming doesn’t save you from writing bad code… all you will be doing is writing bad object-oriented code.

A well-designed library of useful classes is enjoyable and powerful to use (a bad one is not,

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 37

Page 38: The Three Pillars of OOP - sts.rpi.edu · It does so by also introducing how OOP is done inside of C# and .NET. OOP, by itself, is only part of the Object-Oriented picture; there

of course). The .NET BCL and the Java class libraries are examples of such libraries.Your skills in Object-Oriented Programming in large part consists of knowing what’s available in these massive class libraries and putting them to good use.

The Three Pillars of OOP – Copyright © 2007 by Michael F.. Lynch PhD 38