- Overview of Classes
- Programs and Classes: A Quick Example
- Declaring a Class
- Class Members
- Creating Variables and Instances of a Class
- Allocating Memory for the Data
- Instance Members
- Access Modifiers
- Accessing Members from Inside the Class
- Accessing Members from Outside the Class
- Putting It All Together
In the previous chapter, you saw that C# provides six user-defined types. The most important of these, and the one I’ll cover first, is the class. Since the topic of classes in C# is a large one, I’ll spread its discussion over the next several chapters.
Before the days of object-oriented analysis and design, programmers thought of a program as just a sequence of instructions. The focus at that time was on structuring and optimizing those instructions. With the advent of the object-oriented paradigm, the focus changed from optimizing instructions to organizing a program’s data and functions into encapsulated sets of logically related data items and functions, called classes.
A class is a data structure that can store data and execute code. It contains data members and function members:
- Data members store data associated with the class or an instance of the class. Data members generally model the attributes of the real-world object the class represents.
- Function members execute code. These generally model the functions and actions of the real-world object the class represents.
A C# class can have any number of data and function members. The members can be any combination of nine possible member types. Table 4-1 shows these member types. In this chapter I’ll cover fields and methods.
Note Classes are encapsulated sets of logically related data items and functions that generally represent objects in the real world or a conceptual world.
A running C# program is a group of interacting type objects, most of which are instances of classes. For example, suppose you have a program simulating a poker game. When it’s running, it might have an instance of a class called Dealer
, whose job is to run the game, and several instances of a class called Player
, which represent the players of the game.
The Dealer
object stores such information as the current state of the card deck and the number of players. Its actions include shuffling the deck and dealing the cards.
The Player
class is very different. It stores such information as the player’s name and the amount of money left to bet, and it performs actions such as analyzing the player’s current hand and placing bets. Figure 4-1 illustrates the running program. The class names are shown outside the boxes, and the instance names are inside.
A real program would undoubtedly contain dozens of other classes besides Dealer
and Player
. These would include classes such as Card
and Deck
. Each class models some thing that is a component of the poker game.
Note A running program is a set of objects interacting with each other.
Although types int
, double
, and char
are defined in the C# language, classes such as Dealer
and Player
, as you can probably guess, are not defined by the language. If you want to use them in a program, you’ll have to define them yourself. You do this by writing a class declaration.
A class declaration defines the characteristics and members of a new class. It does not create an instance of the class but creates the template from which class instances will be created. The class declaration provides the following:
- The class name
- The members of the class
- The characteristics of the class
The following is an example of the minimum syntax for a class declaration. The curly braces contain the member declarations that make up the class body. Class members can be declared in any order inside the class body. This means it’s perfectly fine for the declaration of a member to refer to another member that is not yet defined until further down in the class declaration.
Keyword Class name
↓ ↓
class MyExcellentClass
{
MemberDeclarations
}
The following code shows the outlines of two class declarations:
class Dealer // Class declaration
{
...
}
class Player // Class declaration
{
...
}
Note Since a class declaration “defines” a new class, you will often see a class declaration referred to as a class definition both in the literature and in common usage among programmers.
Two of the most important class member types are fields and methods. Fields are data members, and methods are function members.
A field is a variable that belongs to a class.
- It can be of any type, either predefined or user-defined.
- Like all variables, fields store data and have the following characteristics:
- They can be written to.
- They can be read from.
The minimum syntax for declaring a field is the following:
Type
↓
Type Identifier;
↑
Field name
For example, the following class contains the declaration of field MyField
, which can store an int
value:
class MyClass
{ Type
↓
int MyField;
↑
} Field name
Note Unlike C and C++, in C# there are no global variables (that is, variables or fields) declared outside of a type. All fields belong to a type and must be declared within their type declaration.
Since a field is a kind of variable, the syntax for a field initializer is the same as that of the variable initializer shown in the previous chapter.
- A field initializer is part of the field declaration and consists of an equal sign followed by an expression that evaluates to a value.
- The initialization value must be determinable at compile time.
class MyClass
{
int F1 = 17;
} ↑
Field initializer- If no initializer is used, the compiler sets the value of a field to a default value, determined by the type of the field. Table 3-1 (in Chapter 3) gives the default values for the simple types. To summarize them: The default value for each type is 0, and
false
forbool
. The default for reference types isnull
.
For example, the following code declares four fields. The first two fields are initialized implicitly. The second two fields are initialized explicitly with initializers.
class MyClass
{
int F1; // Initialized to 0 - value type
string F2; // Initialized to null - reference type
int F3 = 25; // Initialized to 25
string F4 = "abcd"; // Initialized to "abcd"
}
You can declare multiple fields of the same type in the same statement by separating the names with commas. You cannot mix different types in a single declaration. For example, you can combine the four preceding field declarations into two statements, with the exact same semantic result:
int F1, F3 = 25;
string F2, F4 = "abcd";
A method is a named block of executable code that can be executed from many different parts of the program, and even from other programs. (There are also anonymous methods, which aren’t named—but I’ll cover those in Chapter 13.)
When a method is called, or invoked, it executes the method’s code, and then returns to the code that called it and continues executing the calling code. Some methods return a value to the position from which they were called. Methods correspond to member functions in C++.
The minimum syntax for declaring a method includes the following components:
- Return type: This states the type of value the method returns. If a method doesn’t return a value, the return type is specified as
void
.- Name: This is the name of the method.
- Parameter list: This consists of at least an empty set of matching parentheses. If there are parameters (which I’ll cover in the next chapter), they’re listed between the parentheses.
- Method body: This consists of a matching set of curly braces containing the executable code.
For example, the following code declares a class with a simple method called PrintNums
. From the declaration, you can tell the following about PrintNums
:
- The return type is specified as
void
, hence it returns no value.- It has an empty parameter list.
- It contains two lines of code in its method body. The first prints out the number 1 and the second prints out 2.
class SimpleClass
{
Return type Parameter list
↓ ↓
void PrintNums( )
{
Console.WriteLine("1");
Console.WriteLine("2");
}
}
Note Unlike C and C++, in C# there are no global functions (that is, methods or functions) declared outside of a type declaration. Also unlike C and C++, in C# there is no “default” return type for a method. All methods must include a return type or list it as void
.
The class declaration is just the blueprint from which instances of the class are created. Once a class is declared, you can create instances of the class.
- Classes are reference types, which, as you will remember from the previous chapter, means that they require memory both for the reference to the data and for the actual data.
- The reference to the data is stored in a variable of the class type. So, to create an instance of the class, you need to start by declaring a variable of the class type. If the variable isn’t initialized, its value is undefined.
Figure 4-2 illustrates how to define the variable to hold the reference. At the top of the code on the left is a declaration for class Dealer
. Below that is a declaration for class Program
, which contains method Main
. Main
declares variable theDealer
of type Dealer
. Since the variable is uninitialized, its value is undefined, as shown on the right in the figure.
Declaring the variable of the class type allocates the memory to hold the reference, but not the memory to hold the actual data of the class object. To allocate memory for the actual data, you use the new
operator.
- The
new
operator allocates and initializes memory for an instance of the specified type. It allocates the memory from either the stack or the heap, depending on the type.- Use the
new
operator to form an object-creation expression, which consists of the following:
- The keyword
new
.- The name of the type of the instance for which memory is to be allocated.
- Matching parentheses, which might or might not include parameters. I’ll discuss more about the possible parameters later.
Keyword Parentheses are required.
↓ ↓
new TypeName ( )
↑
Type
- If the memory allocated is for a reference type, the object-creation expression returns a reference to the allocated and initialized instance of the object in the heap.
This is exactly what you need to allocate and initialize the memory to hold the class instance data. Use the new
operator to create an object-creation expression, and assign the value returned by it to the class variable. Here’s an example:
Dealer theDealer; // Declare variable for the reference
theDealer = new Dealer(); // Allocate memory for the class object and assign
↑ // it to the variable
Object-creation expression
The code on the left in Figure 4-3 shows the new
operator used to allocate memory and create an instance of class Dealer
, which is then assigned to the class variable. The memory structure is illustrated in the figure, to the right of the code.
You can combine the two steps by initializing the variable with the object-creation expression.
Declare variable
↓
Dealer theDealer = new Dealer(); // Declare and initialize
↑
Initialize with an object-creation expression.
A class declaration acts as a blueprint from which you can create as many instances of the class as you like.
- Instance members: Each instance of a class is a separate entity that has its own set of data members, distinct from the other instances of the same class. These are called instance members since they are associated with an instance of the class.
- Static members: Instance members are the default, but you can also declare members called static members, which are associated with the class, not the instance. I’ll cover these in Chapter 6.
As an example of instance members, the following code shows the poker program with three instances of class Player
. Figure 4-4 shows that each instance has a different value for the Name
field.
class Dealer { ... } // Declare class
class Player { // Declare class
string Name; // Field
...
}
class Program {
static void Main()
{
Dealer theDealer = new Dealer();
Player player1 = new Player();
Player player2 = new Player();
Player player3 = new Player();
...
}
}
From within a class, any function member can access any other member of the class by simply using that member’s name.
The access modifier is an optional part of a member declaration that specifies what other parts of the program have access to the member. The access modifier is placed before the simple declaration forms. The following is the syntax for fields and methods:
Fields
AccessModifier Type Identifier
Methods
AccessModifier ReturnType MethodName ()
{
...
}
The five categories of member access are the following. I’ll describe the first two in this chapter and the others in Chapter 7.
private
public
protected
internal
protected internal
Private members are accessible only from within the class in which they are declared—other classes cannot see or access them.
- Private access is the default access level, so if a member is declared without an access modifier, it is a private member.
- You can also use the
private
access modifier to explicitly declare a member as private. There’s no semantic difference between declaring a private member implicitly as opposed to explicitly. The forms are equivalent.
For example, the following two declarations both specify private
int
members:
int MyInt1; // Implicitly declared private
private int MyInt2; // Explicitly declared private
↑
Access modifier
Public members of an instance are accessible to other objects in the program. You must use the public
access modifier to specify public access.
Access modifier
↓
public int MyInt;
The figures in this text represent classes as labeled boxes, as shown in Figure 4-5.
- The class members are represented as smaller labeled boxes inside the class boxes.
- Private members are represented as enclosed entirely within their class box.
- Public members are represented as sticking partially outside their class box.
Class C1
in the following code declares both public and private fields and methods. Figure 4-6 illustrates the visibility of the members of class C1
.
class C1
{
int F1; // Implicit private field
private int F2; // Explicit private field
public int F3; // Public field
void DoCalc() // Implicit private method
{
...
}
public int GetVal() // Public method
{
...
}
}
As mentioned, members of a class can access the other class members by just using their names.
For example, the following class declaration shows the methods of the class accessing the fields and other methods. Even though the fields and two of the methods are declared private
, all the members of a class can be accessed by any method (or any function member) of the class. Figure 4-7 illustrates the code.
class DaysTemp
{
// Fields
private int High = 75;
private int Low = 45;
// Methods
private int GetHigh()
{
return High; // Access private field
}
private int GetLow()
{
return Low; // Access private field
}
public float Average ()
{
return (GetHigh() + GetLow()) / 2; // Access private methods
} ↑ ↑
} Accessing the private methods
To access a public instance member from outside the class, you must include the variable name and the member name, separated by a period (dot). This is called dot-syntax notation; I’ll describe it in more detail later.
For example, the second line of the following code shows an example of accessing a method from outside the class:
DaysTemp myDt = new DaysTemp(); // Create an object of the class
float fValue = myDt.Average(); // Access it from outside
↑ ↑
Variable name Member name
As an example, the following code declares two classes: DaysTemp
and Program
.
- The two fields in
DaysTemp
are declaredpublic
, so they can be accessed from outside the class.- Method
Main
is a member of classProgram
. It creates a variable and object of classDaysTemp
, and it assigns values to the fields of the object. It then reads the values of the fields and prints them out.
class DaysTemp // Declare class DaysTemp
{
public int High = 75;
public int Low = 45;
}
class Program // Declare class Program
{
static void Main()
{ Variable name
↓
DaysTemp temp = new DaysTemp(); // Create the object
Variable name and field
↓
temp.High = 85; // Assign to the fields
temp.Low = 60; Variable name and field
↓
Console.WriteLine("High: {0}", temp.High ); // Read from fields
Console.WriteLine("Low: {0}", temp.Low );
}
}
This code produces the following output:
High: 85
Low: 60
The following code creates two instances and stores their references in variables named t1
and t2
. Figure 4-8 illustrates t1
and t2
in memory. The code demonstrates the following three actions discussed so far in the use of a class:
- Declaring a class
- Creating instances of the class
- Accessing the class members (that is, writing to a field and reading from a field)
class DaysTemp // Declare the class
{
public int High, Low; // Declare the instance fields
public int Average() // Declare the instance method
{
return (High + Low) / 2;
}
}
class Program
{
static void Main()
{
// Create two instances of DaysTemp
DaysTemp t1 = new DaysTemp();
DaysTemp t2 = new DaysTemp();
// Write to the fields of each instance
t1.High = 76; t1.Low = 57;
t2.High = 75; t2.Low = 53;
// Read from the fields of each instance and call a method of
// each instance
Console.WriteLine("t1: {0}, {1}, {2}",
t1.High, t1.Low, t1.Average() );
Console.WriteLine("t2: {0}, {1}, {2}",
t2.High, t2.Low, t2.Average() );
↑ ↑ ↑
} Field Field Method
}
This code produces the following output:
t1: 76, 57, 66
t2: 75, 53, 64
18.191.181.252