CHAPTER 3

image

Classes 101

Classes are the heart of any application in an object-oriented language. This chapter is broken into several sections. The first section describes the parts of C# that will be used often, and the later sections describe things that won’t be used as often, depending on what kind of code is being written.

A Simple Class

A C# class can be very simple:

class VerySimple
{
    int m_simpleValue = 0;
}
class Test
{
    public static void Main()
    {
       VerySimple vs = new VerySimple();
    }
}

This class is a container for a single integer. Because the integer is declared without specifying how accessible it is, it’s private to the VerySimple class and can’t be referenced outside the class. The private modifier could be specified to state this explicitly.

The integer m_simpleValue is a member of the class; there can be many different types of members, and a simple variable that is part of the class is known as a field.

In the Main() function, the system creates an instance of the class and returns a reference to the instance. A reference is simply a way to refer to an instance.1

There is no need to specify when an instance is no longer needed. In the preceding example, as soon as the Main() function completes, the reference to the instance will no longer exist. If the reference hasn’t been stored elsewhere, the instance will then be available for reclamation by the garbage collector. The garbage collector will reclaim the memory that was allocated when necessary.2

C# FIELD NAMING CONVENTIONS

There are a few common choices for the naming of fields in C# classes:

  • A bare name: “salary
  • A name preceded by an underscore: “_salary
  • A name preceded by “m_”: “m_salary
  • An uppercase name preceded by “m_”: “m_Salary

In the early days of .NET and C#, there was a conscious decision to move far away from the Hungarian notation common in C/C++ code, the convention that gave us names such as lpszName. Most of the early code that I wrote3 used the bare name syntax, but since then I’ve been in groups that have used the other syntaxes and have written a fair amount of code in all three.

While it is true that modern IDEs have made it much easier to understand the type of a variable with minimal effort, I still find it very useful to know which variables are instance variables and which ones are local variables or parameters. I am also not a fan of having to use “this.” in constructors to disambiguate.

I preferred the second syntax for a while but have since converted to using the third syntax, which coincidentally (or perhaps not given the time I spent on the VC++ team) is the same syntax used by the Microsoft Foundation Class libraries.

This is all very nice, but this class doesn’t do anything useful because the integer isn’t accessible. Here’s a more useful example:4

using System;
class Point
{
       // constructor
    public Point(int x, int y)
    {
       m_x = x;
       m_y = y;
    }
       // member fields
    public int m_x;
    public int m_y;
}
class Test
{
    public static void Main()
    {
       Point myPoint = new Point(10, 15);
       Console.WriteLine("myPoint.x {0}", myPoint.m_x);
       Console.WriteLine("myPoint.y {0}", myPoint.m_y);
    }
}

In this example, there is a class named Point, with two integers in the class named m_x and m_y. These members are public, which means that their values can be accessed by any code that uses the class.

In addition to the data members, there is a constructor for the class, which is a special function that is called to help construct an instance of the class. The constructor takes two integer parameters. It is called in the Main() method.

In addition to the Point class, there is a Test class that contains a Main function that is called to start the program. The Main function creates an instance of the Point class, which will allocate memory for the object and then call the constructor for the class. The constructor will set the values for m_x and m_y. The remainder of the lines of Main() print out the values of m_x and m_y.

In this example, the data fields are accessed directly. This is usually a bad idea, because it means that users of the class depend on the names of fields, which constrains the modifications that can be made later.

In C#, rather than writing a member function to access a private value, a property would be used, which gives the benefits of a member function while retaining the user model of a field. Chapter 16 discusses properties in more detail.

Member Functions

The constructor in the previous example is an example of a member function; a piece of code that is called on an instance of the object. Constructors can only be called automatically when an instance of an object is created with new.

Other member functions can be declared as follows:

using System;
class Point
{
    public Point(int x, int y)
    {
       m_x = x;
       m_y = y;
    }
       // accessor functions
    public int GetX() {return m_x;}
    public int GetY() {return m_y;}

       // variables now private
    int m_x;
    int m_y;
}
class Test
{
    public static void Main()
    {
       Point myPoint = new Point(10, 15);
       Console.WriteLine("myPoint.X {0}", myPoint.GetX());
       Console.WriteLine("myPoint.Y {0}", myPoint.GetY());
    }
}

ref and out Parameters

Having to call two member functions to get the values may not always be convenient, so it would be nice to be able to get both values with a single function call.There’s only one return value, however.

One solution is to use reference (or ref) parameters, so that the values of the parameters passed into the member function can be modified:

using System;
class Point
{
    public Point(int x, int y)
    {
       m_x = x;
       m_y = y;
    }
       // get both values in one function call
    public void GetPoint(ref int x, ref int y)
    {
       x = m_x;
       y = m_y;
    }
    int m_x;
    int m_y;
}
class Test
{
    public static void Main()
    {
       Point myPoint = new Point(10, 15);
       int x;
       int y;

            // illegal
       myPoint.GetPoint(ref x, ref y);
       Console.WriteLine("myPoint({0}, {1})", x, y);
    }
}

In this code, the parameters have been declared using the ref keyword, as has the call to the function.

This code appears to be correct, but when compiled, it generates an error message that says that uninitialized values were used for the ref parameters x and y. This means that variables were passed into the function before having their values set, and the compiler won’t allow the values of uninitialized variables to be exposed.

There are two ways around this. The first is to initialize the variables when they are declared:

using System;
class Point
{
    public Point(int x, int y)
    {
       m_x = x;
       m_y = y;
    }
    public void GetPoint(ref int x, ref int y)
    {
       x = m_x;
       y = m_y;
    }
    int m_x;
    int m_y;
}
class Test
{
    public static void Main()
    {
       Point myPoint = new Point(10, 15);
       int x = 0;
       int y = 0;

       myPoint.GetPoint(ref x, ref y);
       Console.WriteLine("myPoint({0}, {1})", x, y);
    }
}

The code now compiles, but the variables are initialized to zero only to be overwritten in the call to GetPoint(). For C#, another option is to change the definition of the function GetPoint() to use an out parameter rather than a ref parameter:

using System;
class Point
{
    public Point(int x, int y)
    {
       m_x = x;
       m_y = y;
    }
    public void GetPoint(out int x, out int y)
    {
       x = m_x;
       y = m_y;
    }
    int m_x;
    int m_y;
}
class Test
{
    public static void Main()
    {
       Point myPoint = new Point(10, 15);
       int x;
       int y;

       myPoint.GetPoint(out x, out y);
       Console.WriteLine("myPoint({0}, {1})", x, y);
    }
}

Outparameters are exactly like ref parameters except that an uninitialized variable can be passed to them, and the call is made with out rather than ref.5

image Note  It’s fairly uncommon to use ref or out parameters in C#. If you find yourself wanting to use them, I suggest taking a step back and seeing if there isn’t a better solution.

Overloading

Sometimes it may be useful to have two functions that do the same thing but take different parameters. This is especially common for constructors, when there may be several ways to create a new instance.

class Point
{
       // create a new point from x and y values
    public Point(int x, int y)
    {
       m_x = x;
       m_y = y;
    }
       // create a point from an existing point
    public Point(Point p)
    {
       m_x = p.m_x;
       m_y = p.m_y;
    }
    int m_x;
    int m_y;
}
class Test
{
    public static void Main()
    {
       Point myPoint = new Point(10, 15);
       Point mySecondPoint = new Point(myPoint);
    }
}

The class has two constructors: one that can be called with x and y values, and one that can be called with another point. The Main() function uses both constructors: one to create an instance from an x and y value, and another to create an instance from an already-existing instance.6

When an overloaded function is called, the compiler chooses the proper function by matching the parameters in the call to the parameters declared for the function.

1 For those of you used to pointers, a reference is a pointer that you can only assign to and dereference.

2 The garbage collector used in the .NET Runtime is discussed in Chapter 39. At this point it is reasonable to just assume that it handles all the memory for you.

3 Including the code in earlier versions of this book.

4 If you were really going to implement your own point class, you’d probably want it to be a value type (struct). We’ll talk more about structs in Chapter 8.

5 From the perspective of other .NET languages, there is no difference between ref and out parameters. A C# program calling this function will see the parameters as out parameters, but other languages will see them as ref parameters.

6 This function may look like a C++ copy constructor, but the C# language doesn’t use such a concept. A constructor such as this must be called explicitly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.198.60