Monday, February 4, 2013

6.2. Objects and classes in C#


Let's see the details. Classes in C# are defined in a similar way to structs, but now they will include functions as well. This way, the class "Door" might be programmed like this:

public class Door
{

  int width;     // Width in centimeters
  int height;    // Height in centimeters
  int color;     // Color in RGB format
  bool open;     // Open or closed
 
  public void Open()
  {
    open = true;
  }
 
  public void Close()
  {
    open = false;
  }

  public void ShowStatus()
  {
    Console.WriteLine("Width: {0}", width);
    Console.WriteLine("Height: {0}", height);
    Console.WriteLine("Color: {0}", color);
    Console.WriteLine("Open: {0}", open);
  }
   
} // End of class Door  


As you can see, the objects of class "Door" will have a width, height, color, and status (open or closed), and also will be able to open or close (and besides, we can "show their status", to check that everything works correctly).

To declare these objects that belong to the class "Door", we use the word "new", like we did with "arrays":

    Door p = new Door();
    p.Open();
    p.ShowStatus();

We will create a test program that uses an object of this class (a "Door"):

/*---------------------------*/
/*  C# Example #59:          */
/*  example59.cs             */
/*                           */
/*  First classes example    */
/*                           */
/*  Intro to C#,             */
/*    Nacho Cabanes          */
/*---------------------------*/

using System;

public class Door
{

  int width;     // Width in centimeters
  int height;    // Height in centimeters
  int color;     // Color in RGB format
  bool open;     // Open or closed
   
  public void Open()
  {
    open = true;
  }
 
  public void Close()
  {
    open = false;
  }

  public void ShowStatus()
  {
    Console.WriteLine("Width: {0}", width);
    Console.WriteLine("Height: {0}", height);
    Console.WriteLine("Color: {0}", color);
    Console.WriteLine("Open: {0}", open);
  }
   
} // End of class Door  


public class Example59
{
 
  public static void Main()
  {
    Door d = new Door();
   
    Console.WriteLine("Starting values...");
    d.ShowStatus();
   
    Console.WriteLine("\nLet's open...");
    d.Open();
    d.ShowStatus();
  }
 
}

This source program does not contain only one class, as the previous examples, but two different classes:

·         The class "Door", which is the new kind of objects that we are going to practice with.
·         The class "Example59", which is our application.

Its result is:

Starting values...
Width: 0
Height: 0
Color: 0
Open: False

Let's open...
Width: 0
Height: 0
Color: 0
Open: True

As you can see, in C#, unlike in other languages, variables that are part of a class (the "attributes") have a value preset value: 0 for numbers and an empty string for strings, or "false" for boolean data.

We also see that we can access the methods and data preceding the name of each by the name of the variable and a dot, as we did with records (struct). Still, in this example we can not do directly "p.open = true" for two reasons:

·         The attribute "open" does not have the word "public" before it, so it is not public, but private and will not be accessible from other classes (in our case, from Example59).
·         OOP purists recommend not to access the attributes directly, but using helper methods to change them (as our method "Open"), and read its value also using a function. This is called "data hiding". It has benefits, such as the ability to change the internals of our class without affecting its use.

Usually, as a way to hide data, we will create auxiliary functions called setXXX and GetXXX that provide access to the attribute (in C# there is an alternative way to achieve the same result, using "properties", as we will see later):

  public int GetWidth()
  {
    return width;
  }
 
  public void SetWidth(int newValue)
  {
    width = newValue;
  }

It can also seem surprising to see the word "static" in "Main", but not in the methods of the class "Door". We will see the reason a little later.

Suggested exercise:
·         (6.2.1) Create a class called Person, in a file "person.cs." This class should have an attribute "name", of type string. It will also need a method "SetName", with type void and a string parameter, that allows the user to change the value of the name. At last, it will also have a method called "Greet", which will display "Hello," followed by ther name. Create another class called TestPerson. This class should contain only the Main function, which will create two objects of type Person, assign them a name and ask them to greet.


In a large project, it is recommended that each class is in its own source file, so that they can be easily found (in our previous examples it was not necessary because they were very simple). For this reason, it is interesting (but not required) to store each class in a file with the same name: for example, the class Door in a file called "Door.cs." We will not follow this is a rule in some of the examples, to keep a consecutive numbering.

If we want to compile a program consisting of several sources, we just need to specify the names of them all. For example, using Mono it would be:

gmcs source1.cs source2.cs source3.cs

In this case, the executable name is that of the first sources (source1.exe). We can change the name of the executable with the "-out" option of Mono:

gmcs source1.cs source2.cs source3.cs -out:example.exe


Let's split the last example into two sources, and see how to compile it. The first class might be:

/*---------------------------*/
/*  C# Example #59b:         */
/*  example59b.cs            */
/*                           */
/*  Two classes in two       */
/*  files (file 1)           */
/*                           */
/*  Intro to C#,             */
/*    Nacho Cabanes          */
/*---------------------------*/

using System;

public class Door
{

  int width;     // Width in centimeters
  int height;    // Height in centimeters
  int color;     // Color in RGB format
  bool open;     // Open or closed
   
  public void Open()
  {
    open = true;
  }
 
  public void Close()
  {
    open = false;
  }

  public void ShowStatus()
  {
    Console.WriteLine("Width: {0}", width);
    Console.WriteLine("Height: {0}", height);
    Console.WriteLine("Color: {0}", color);
    Console.WriteLine("Open: {0}", open);
  }
   
} // End of class Door  


And the second class might be

/*---------------------------*/
/*  C# Example #59c:         */
/*  example59c.cs            */
/*                           */
/*  Two classes in two       */
/*  files (file 1)           */
/*                           */
/*  Intro to C#,             */
/*    Nacho Cabanes          */
/*---------------------------*/

public class Example59c
{
 
  public static void Main()
  {
    Door p = new Door();
   
    Console.WriteLine("Starting values ...");
    p.ShowStatus();
   
    Console.WriteLine("\nLet's open...");
    p.Open();
    p.ShowStatus();
  }
 
}

And we would compile it using:

gmcs example59b.cs example59c.cs -out:example59bAndC.exe


Anyway, for these projects consisting of various classes, it is preferable to use a more advanced environment like VisualStudio or SharpDevelop, which allow creating all classes comfortably, working on several classes at a time, mark the error lines inside the editor, and so on. Because of that, at the end of this chapter there is a section with an introduction to using SharpDevelop.


Suggested exercise:
·         (6.2.2) Modify the program that you wrote in the previous exercise, to split it in two different files: Create a class called Person, in a file "person.cs." This class should have an attribute "name", of type string. It will also need a method "SetName", with type void and a string parameter, to allows the user to change the value of the name. At last, it will also have a method called "Greet", which will display "Hello," followed by their name. Create another class called TestPerson, in the file "testPerson.cs". This class should contain only the Main function, which will create two objects of type Person, assign them a name and ask them to greet.