Mr. Martin's programming school

Classes

« Functions Error handling »
You might think: 'What do I need classes for? Don't I have all the tools I need to write programs?'

Well, sometimes programs gets large and complex. Then they get hard to understand, to correct, to add functionality to, in essence, hard to maintain.

Also, you will find yourself coding things you know you have coded before, in some version in some other program. Or, you know somebody else did that, so why do you need to do it if you can have that somebody's code?

In order to do that it would be nice to be able to wrap it up somehow.

Basically, a class is a bunch of source code and declaration of variables. You have already used somebody else's code when you used the object System.Console.

Wait a minute, 'object'? You said it is all about 'classes'!

Yes, but it is almost the same thing. A class contains source code and variables, while an object is the compiled result of that class. You did not see the source code for System.Console. You only had the machine code available to link into your program, and that is because your sorce code contains the text 'using System;'. (Go check you initial console programs. Remove the line 'using System;' and see for yourself what happens.)

So, the class is describing it all, and the object is what you create when you declare your object, in order to actually beeing able use it, like you declare an Int32 object in order to be able to store numbers in it.

Hey! What was that? Int32 object? All primitive variables are handeled by the compiler. If you say 'string' it reserves memory space for characters and associates your variable name with the first character. If you say 'int' it reserves, let me see now, what kind of computer do i have... Well, historically the actual size of an int was depending on the processor architecture. An 8-bit processor had 8-bit int variables, while a 16-bit processor had 16-bit ditto etc.

Obviously that was not very practical, and not portable. If you wrote a program for one type of architecture, it would probably compile, but might not run properly on another. Also, if you had a 32-bit machine and wanted a 16 bit integer? Then they invented types like 'short int' and 'long int' to order int variables of a size that was different to the processor architecture. But still, a short int on a 32-bit processor is 16 bits, but on a 16-bit processor it is 8 bits.

To remedy that, C# introduces the classes Int16, Int32, Int64 to handle integers independent of the processor architecture. But that is not enough. I said 'classes', not 'types', and that is for a reason. The new types are actually classes. You can see that on the coloring in Visual Studio. Go on and try it. Declare an int and an Int32 variable and see what happens. The intrinsic types, like 'int' are blue, while Int32 etc. are light blue, like all class types.

Also check what intellisense has to say about an Int32 type (press Alt + Space). Lots of stuff on the Int32:



So, in addition to holding a 32-bit integer Int32 has four methods and two constants you can use. As you can see, intellisense also provides information on what the metods do, and what they return. Actually the full syntax.

However, the compiler provides new services also on the old intrinsic types:



But remember, they are still not the same size on different architectures.

Even if you use intellisense on a variable of any type you do get som extra stuff:



Let's try the Parse method:



This will work fine. Try this exercise:

Int32.Parse exercise


A class is like a type, but it is a complex type. Not just an integer or a string. It might be describing something that needs to be described in terms of numbers and names, i.e. more than one simple type. If you e.g. would like to describe a vehicle, you migth want to tell the number of wheels, the name of the vehicle, the type or whatever describes a vehicle. A bike has two wheels and is called a 'Bike' and is of a 'Recreational' type, while a car has e.g. four wheels and is called a 'Car' and is of type 'Personal engine-driven' vehicle.

The class contains the attrubutes you need, like the number or wheels, type and name in the vehicle class above.

The class may also contains functions that performs any needed operations. Like a car that runs so many miles to the gallon, how much fuel will it need to take you a given number of miles?

The object is your instansiation of the class. That is nerd talk, 'instansiation' is exactly the same as when you declare a variable. You may declare it exactly the same way you declare an integer:

Int32 counter;
Vehicle myCar;
Vehicle companyCar;

As I mentioned, the class may also contain functions that performs any needed operations. One operation might be to set initial values to an attrubute, a variable of the class. This can be done by calling a function in the class, of course, but there is a better way. A class can have a special 'constructor' function. That function will be called when creating the object, if you create it a different way (in order to have somewhere to supply arguments):

Vehicle myBike = new Vehicle(2, "My bike", "Recreational");
Vehicle myCar = new Vehicle(4, "My company car", "Personal engine-driven");
Vehicle bigTruck = new Vehicle(12, "Caterpillar", "Heavy duty road maintenance");

A real class example

Let's make a real class that could be useful in different contexts. Let's make a conversion class, a class that converts between tempreatures in Farenheit and Celcius. We will also take a look at different ways to do this class.

First we create a class concentrating on the conversion only:

class FarenheitCelciusConverter
{
    public Double FarehnetToCelcius (Double Fahrenheit)
    {
        return (Fahrenheit - 32.0) / 1.8;
    }
    public Double CelciusToFarehnet (Double Celcius)
    {
        return 32.0 + Celcius * 1.8;
    }
}

This one is really simple to use. You create an instance (you declare a variable) and call its methods:

FarenheitCelciusConverter fcc = new FarenheitCelciusConverter();
Double farenheit = fcc.CelciusToFarehnet(40.0);
Console.WriteLine(farenheit.ToString()); // Just for a sample output
Double celcius = fcc.FarehnetToCelcius(104.0);
Console.WriteLine(celcius.ToString()); // Just for a sample output

Why did we have to use the keyword 'new' here? Well, that is when the actual object is created. If you type e.g.

Int32 myInteger;

...you only reserve memory space for an integer. You do not assign it any value.

When you do the same thing with a class as type, you will have an object with a null value, i.e. it is not really crated, and has no memory space.

The 'new' keyword creates space in memory, assign the address of that memory space to the new object, and calls any available constructor. (We did not supply any constructor in this example.)

A class with a state

Let us assume that we want a class that always represent a certain temperature. In the previous class we only supplied conversion methods (that is what we call functions in a class when they can be called upon).

Did you notice the keyword 'public' used when declairing the methods of our class FarenheitCelciusConverter? That keyword made it possible to call those methods on the object.

In the next example we will change the class so that it represents a certain temperature, and that it returns Farenheit and Celcius values calculated from that internal temperature value. To make it interesting, let us store the temperatuer as degrees Kelvin (that is the absolute zero temprerature, 273.2 degrees Celcius).

class TemperatureConverter
{
    private Double Kelvin;
    public TemperatureConverter (Double kelvin)
    {
        this.Kelvin = kelvin;
    }
    public Double Celcius ()
    {
        return Kelvin + 273.2;
    }
    public Double Farehnet ()
    {
        return Kelvin + 305.2 * 1.8;
    }
}

Now you can create an object of a certain temperature and ask for the tempreature in Farenheit and in Celcius.
Still, why not make it possible to change the temperatuer represented by the object? Well, that is easy! You could create a method that does exactly that, but current programming school teaches an easier standard: getters and setters.

class TemperatureConverter
{
    public Double Kelvin { get; set; }
    public TemperatureConverter (Double kelvin)
    {
        this.Kelvin = kelvin;
    }
    public Double Celcius ()
    {
        return Kelvin + 273.2;
    }
    public Double Farehnet ()
    {
        return Kelvin + 305.2 * 1.8;
    }
}

As simple as that! Now you can change the Kelvin value by assigning it a new one.

TemperatureConverter tc = new TemperatureConverter(305.2);
Console.WriteLine(tc.Celcius); // Prints temperatuer in Celcius.
Console.WriteLine(tc.Farenheit); // Prints temperature in Fahrenheit.
tc.Kelvin = 273.2; // Set Kelvin temperature to water freezing point.
Console.WriteLine(tc.Celcius); // Prints water freezing point in Celcius = 0.0 degrees Celcius.

Do we really need to use Kelvin at all? No, of course not. You can base the class on Farenheit:

class TemperatureConverter
{
    public Double Fahrenheit { get; set; }
    public TemperatureConverter (Double fahrenheit)
    {
        this.Fahrenheit = fahrenheit;
    }
    public Double Celcius ()
    {
        return (Fahrenheit - 32.0) / 1.8;
    }
    public Double Farehnet ()
    {
        return Fahrenheit;
    }
}

...or on Celcius:

class TemperatureConverter
{
    public Double Celcius { get; set; }
    public TemperatureConverter (Double celcius)
    {
        this.Celcius = celcius;
    }
    public Double Celcius ()
    {
        return Celcius;
    }
    public Double Farehnet ()
    {
        return (Fahrenheit - 32.0) / 1.8;
    }
}

Do I really have to give the object a value from start when it anyway is possible to assigne a value later? No, of course not. But one thing is important. If no value is assigned, you can still call the methods, and then what will you get? An undefind result at best, or an error.

One way is to assign a value at instation:

class TemperatureConverter
{
    public Double Celcius { get; set; }
    public TemperatureConverter (Double celcius = 0.0)
    {
        this.Celcius = celcius;
    }
    public Double Celcius ()
    {
        return Celcius;
    }
    public Double Farehnet ()
    {
        return (Fahrenheit - 32.0) / 1.8;
    }
}

Note the assignment in the argument list. The argument celcius will now get the value 0.0 unless you give it another value:

TemperatureConverter tc1 = new TemperatureConverter(); // tc1 will have the temperature of 0.0 degrees Celcius.
TemperatureConverter tc2 = new TemperatureConverter(40.0); // tc2 will have the temperature of 40.0 degrees Celcius.

So, actually you now logically have two constructors. One that starts with a default value of 0.0 degree Celcius and one that expects a temperauter in degree Celcius. This is called polymorfism, multi-form. A method, in this case the constructor of the class TemperatureConverter has two forms.

Other methods may also have multiple forms.

You can of course do the same thing with a TemperatureConverter that takes a Farenheit temperature as argument in the constructor.