Creating and Managing Classes and Objects - Microsoft® Visual C#® 2012 Step by Step (2012)

Microsoft® Visual C#® 2012 Step by Step (2012)

Chapter 7. Creating and Managing Classes and Objects

After completing this chapter, you will be able to

§ Define a class containing a related set of methods and data items.

§ Control the accessibility of members by using the public and private keywords.

§ Create objects by using the new keyword to invoke a constructor.

§ Write and call your own constructors.

§ Create methods and data that can be shared by all instances of the same class by using the static keyword.

§ Explain how to create anonymous classes.

The Microsoft Windows Runtime for Windows 8 together with the Microsoft .NET Framework available on Windows 7 and Windows 8 contain thousands of classes, and you have used a number of them already, including Console and Exception. Classes provide a convenient mechanism for modeling the entities manipulated by applications. An entity can represent a specific item, such as a customer, or something more abstract, such as a transaction. Part of the design process of any system is concerned with determining the entities that are important to the processes that the system implements, and then performing an analysis to see what information these entities need to hold and what operations they should perform. You store the information that a class holds as fields and use methods to implement the operations that a class can perform.

Understanding Classification

Class is the root word of the term classification. When you design a class, you systematically arrange information and behavior into a meaningful entity. This arranging is an act of classification and is something that everyone does—not just programmers. For example, all cars share common behaviors (they can be steered, stopped, accelerated, and so on) and common attributes (they have a steering wheel, an engine, and so on). People use the word car to mean an object that shares these common behaviors and attributes. As long as everyone agrees on what a word means, this system works well and you can express complex but precise ideas in a concise form. Without classification, it’s hard to imagine how people could think or communicate at all.

Given that classification is so deeply ingrained in the way we think and communicate, it makes sense to try to write programs by classifying the different concepts inherent in a problem and its solution and then modeling these classes in a programming language. This is exactly what you can do with modern object-oriented programming languages, such as Microsoft Visual C#.

The Purpose of Encapsulation

Encapsulation is an important principle when defining classes. The idea is that a program that uses a class should not have to worry how that class actually works internally; the program simply creates an instance of a class and calls the methods of that class. As long as those methods do what they say they will do, the program does not care how they are implemented. For example, when you call the Console.WriteLine method, you don’t want to be bothered with all the intricate details of how the Console class physically arranges for data to be written to the screen. A class might need to maintain all sorts of internal state information to perform its various methods. This additional state information and activity is hidden from the program that is using the class. Therefore, encapsulation is sometimes referred to as information hiding. Encapsulation actually has two purposes:

§ To combine methods and data inside a class; in other words, to support classification

§ To control the accessibility of the methods and data; in other words, to control the use of the class

Defining and Using a Class

In C#, you use the class keyword to define a new class. The data and methods of the class occur in the body of the class between a pair of braces. Here is a C# class called Circle that contains one method (to calculate the circle’s area) and one piece of data (the circle’s radius):

class Circle

{

int radius;

double Area()

{

return Math.PI * radius * radius;

}

}

NOTE

The Math class contains methods for performing mathematical calculations and fields containing mathematical constants. The Math.PI field contains the value 3.14159265358979323846, which is an approximation of the value of Pi.

The body of a class contains ordinary methods (such as Area) and fields (such as radius)—remember that variables in a class are called fields. You’ve already seen how to declare variables in Chapter 2, and how to write methods in Chapter 3, so there’s almost no new syntax here.

You can use the Circle class in a similar manner to using the other types that you have already met. You create a variable specifying Circle as its type, and then you initialize the variable with some valid data. Here is an example:

Circle c; // Create a Circle variable

c = new Circle(); // Initialize it

A point worth highlighting in this code is the use of the new keyword. Previously, when you initialized a variable such as an int or a float, you simply assigned it a value:

int i;

i = 42;

You cannot do the same with variables of class types. One reason for this is that C# just doesn’t provide the syntax for assigning literal class values to variables. You cannot write a statement such as this:

Circle c;

c = 42;

After all, what is the Circle equivalent of 42? Another reason concerns the way in which memory for variables of class types is allocated and managed by the runtime—this is discussed further in Chapter 8 For now, just accept that the new keyword creates a new instance of a class, more commonly called an object.

You can, however, directly assign an instance of a class to another variable of the same type, like this:

Circle c;

c = new Circle();

Circle d;

d = c;

However, this is not as straightforward as it first appears, for reasons that that are described in Chapter 8.

IMPORTANT

Don’t confuse the terms class and object. A class is the definition of a type. An object is an instance of that type, created when the program runs. Several objects can be instances of the same class.

Controlling Accessibility

Surprisingly, the Circle class is currently of no practical use. By default, when you encapsulate your methods and data inside a class, the class forms a boundary to the outside world. Fields (such as radius) and methods (such as Area) defined in the class can be seen by other methods inside the class but not by the outside world—they are private to the class. So, although you can create a Circle object in a program, you cannot access its radius field or call its Area method, which is why the class is not of much use—yet! However, you can modify the definition of a field or method with the public and private keywords to control whether it is accessible from the outside:

§ A method or field is private if it is accessible only from the inside of the class. To declare that a method or field is private, you write the keyword private before its declaration. As intimated previously, this is actually the default, but it is good practice to state explicitly that fields and methods are private to avoid any confusion.

§ A method or field is public if it is accessible from both the inside and outside of the class. To declare that a method or field is public, you write the keyword public before its declaration.

Here is the Circle class again. This time, Area is declared as a public method and radius is declared as a private field:

class Circle

{

private int radius;

public double Area()

{

return Math.PI * radius * radius;

}

}

NOTE

C++ programmers should note that there is no colon after the public and private keywords. You must repeat the keyword for every field and method declaration.

Although radius is declared as a private field and is not accessible from outside the class, radius is accessible from inside the Circle class. The Area method is inside the Circle class, so the body of Area has access to radius. However, the class is still of limited value because there is no way of initializing the radius field. To fix this, you can use a constructor.

TIP

Unlike variables declared in a method that are not initialized by default, the fields in a class are automatically initialized to 0, false, or null depending on their type. However, it is still good practice to provide an explicit means of initializing fields.

NAMING AND ACCESSIBILITY

Many organizations have their own house-style that they ask developers to follow when writing code. Part of this style typically involves rules for naming identifiers, and the purpose of these rules is typically to assist in the maintainability of the code. The following recommendations are reasonably common and relate to the naming conventions for fields and methods based on the accessibility of class members; however, C# does not enforce these rules:

§ Identifiers that are public should start with a capital letter. For example, Area starts with A (not a) because it’s public. This system is known as the PascalCase naming scheme (because it was first used in the Pascal language).

§ Identifiers that are not public (which include local variables) should start with a lowercase letter. For example, radius starts with r (not R) because it’s private. This system is known as the camelCase naming scheme.

NOTE

Some organizations use camelCase only for methods and adopt the convention that private fields are named starting with an initial underscore character, such as _radius. However, the examples in this book use camelCase for private methods and fields.

There’s only one exception to this rule:class names should start with a capital letter, and constructors must match the name of their class exactly; therefore, a private constructor must start with a capital letter.

IMPORTANT

Don’t declare two public class members whose names differ only in case. If you do, developers using other languages that are not case sensitive, such as Microsoft Visual Basic, may not be able to integrate your class into their solutions.

Working with Constructors

When you use the new keyword to create an object, the runtime has to construct that object by using the definition of the class. The runtime has to grab a piece of memory from the operating system, fill it with the fields defined by the class, and then invoke a constructor to perform any initialization required.

A constructor is a special method that runs automatically when you create an instance of a class. It has the same name as the class, and it can take parameters, but it cannot return a value (not even void). Every class must have a constructor. If you don’t write one, the compiler automatically generates a default constructor for you. (However, the compiler-generated default constructor doesn’t actually do anything.) You can write your own default constructor quite easily—just add a public method that does not return a value and give it the same name as the class. The following example shows the Circle class with a default constructor that initializes the radius field to 0:

class Circle

{

private int radius;

public Circle() // default constructor

{

radius = 0;

}

public double Area()

{

return Math.PI * radius * radius;

}

}

NOTE

In C# parlance, the default constructor is a constructor that does not take any parameters. It does not matter whether the compiler generates it or you write it, it is still the default constructor. You can also write nondefault constructors (constructors that do take parameters), as you will see in the upcoming section titled Overloading Constructors.

In this example, the constructor is marked as public. If this keyword is omitted, the constructor will be private (just like any other methods and fields). If the constructor is private, it cannot be used outside the class, which prevents you from being able to create Circle objects from methods that are not part of the Circle class. You might therefore think that private constructors are not that valuable. They do have their uses, but they are beyond the scope of the current discussion.

Having added a public constructor, you can now use the Circle class and exercise its Area method. Notice how you use dot notation to invoke the Area method on a Circle object:

Circle c;

c = new Circle();

double areaOfCircle = c.Area();

Overloading Constructors

You’re almost finished, but not quite. You can now declare a Circle variable, use it to reference a newly created Circle object, and then call its Area method. However, there is one last problem. The area of all Circle objects will always be 0 because the default constructor sets the radius to 0 and it stays at 0; the radius field is private, and there is no easy way of changing its value after it has been initialized. A constructor is just a special kind of method and it—like all methods—can be overloaded. Just as there are several versions of the Console.WriteLine method, each of which takes different parameters, so too can you write different versions of a constructor. So, you can add another constructor to the Circle class, with a parameter that specifies the radius to use, like this:

class Circle

{

private int radius;

public Circle() // default constructor

{

radius = 0;

}

public Circle(int initialRadius) // overloaded constructor

{

radius = initialRadius;

}

public double Area()

{

return Math.PI * radius * radius;

}

}

NOTE

The order of the constructors in a class is immaterial; you can define constructors in whatever order you feel most comfortable with.

You can then use this constructor when creating a new Circle object, like this:

Circle c;

c = new Circle(45);

When you build the application, the compiler works out which constructor it should call based on the parameters that you specify to the new operator. In this example, you passed an int, so the compiler generates code that invokes the constructor that takes an int parameter.

You should be aware of an important feature of the C# language: if you write your own constructor for a class, the compiler does not generate a default constructor. Therefore, if you’ve written your own constructor that accepts one or more parameters and you also want a default constructor, you’ll have to write the default constructor yourself.

PARTIAL CLASSES

A class can contain a number of methods, fields, and constructors, as well as other items discussed in later chapters. A highly functional class can become quite large. With C#, you can split the source code for a class into separate files so that you can organize the definition of a large class into smaller, easier to manage pieces. This feature is used by Microsoft Visual Studio 2012 for Windows Presentation Foundation (WPF) and Windows Store apps, where the source code that the developer can edit is maintained in a separate file from the code that is generated by Visual Studio whenever the layout of a form changes.

When you split a class across multiple files, you define the parts of the class by using the partial keyword in each file. For example, if the Circle class is split between two files called circ1.cs (containing the constructors) and circ2.cs (containing the methods and fields), the contents of circ1.cs look like this:

partial class Circle

{

public Circle() // default constructor

{

this.radius = 0;

}

public Circle(int initialRadius) // overloaded constructor

{

this.radius = initialRadius;

}

}

The contents of circ2.cs look like this:

partial class Circle

{

private int radius;

public double Area()

{

return Math.PI * this.radius * this.radius;

}

}

When you compile a class that has been split into separate files, you must provide all the files to the compiler.

In the following exercise, you will declare a class that models a point in two-dimensional space. The class will contain two private fields for holding the x- and y-coordinates of a point and will provide constructors for initializing these fields. You will create instances of the class by using thenew keyword and calling the constructors.

Write constructors and create objects

1. Start Visual Studio 2012 if it is not already running.

2. Open the Classes project located in the \Microsoft Press\Visual CSharp Step By Step\Chapter 7\Windows X\Classes folder in your Documents folder.

3. In Solution Explorer, double-click the file Program.cs to display it in the Code and Text Editor window.

4. Locate the Main method in the Program class.

The Main method calls the doWork method, wrapped in a try block and followed by a catch handler. With this try/catch block, you can write the code that would typically go inside Main in the doWork method instead, safe in the knowledge that it will catch and handle any exceptions. The doWork method currently contains nothing but a // TODO: comment.

TIP

TODO comments are frequently used by developers as a reminder that they have left a piece of code to come back to, and they frequently have a description of the work to be performed, such as // TODO:Implement the doWork method. Visual Studio recognizes this form of comment, and you can quickly locate them anywhere in an application by using the Task List window. To display this window, on the VIEW menu click Task List. The Task List window appears below the Code and Text Editor window by default. In the drop-down list box at the top of this window, select Comments. All of the TODO comments will be listed. You can then double-click any of these comments and you will be taken directly to the corresponding code, which will be displayed in the Code and Text Editor window.

image with no caption

5. Display the file Point.cs in the Code and Text Editor window.

This file defines a class called Point, which you will use to represent the location of a point in two-dimensional space, defined by a pair of x- and y-coordinates. The Point class is currently empty apart from another // TODO: comment.

6. Return to the Program.cs file. Edit the body of the doWork method in the Program class, and replace the // TODO: comment with the following statement:

Point origin = new Point();

This statement creates a new instance of the Point class and invokes its default constructor.

7. On the BUILD menu, click Build Solution.

The code builds without error because the compiler automatically generates the code for a default constructor for the Point class. However, you cannot see the C# code for this constructor because the compiler does not generate any source language statements.

8. Return to the Point class in the file Point.cs. Replace the // TODO: comment with a public constructor that accepts two int arguments called x and y and that calls the Console.WriteLine method to display the values of these arguments to the console, as shown in bold type in the following code example:

9. class Point

10.{

11. public Point(int x, int y)

12. {

13. Console.WriteLine("x:{0}, y:{1}", x, y);

14. }

}

NOTE

Remember that the Console.WriteLine method uses {0} and {1} as placeholders. In the statement shown, {0} will be replaced with the value of x, and {1} will be replaced with the value of y when the program runs.

15.On the BUILD menu, click Build Solution.

The compiler now reports an error:

'Classes.Point' does not contain a constructor that takes 0 arguments

The call to the default constructor in the doWork method is now invalid because there is no longer a default constructor. You have written your own constructor for the Point class, so the compiler does not generate the default constructor. You will now fix this by writing your own default constructor.

16.Edit the Point class, and add a public default constructor that calls Console.WriteLine to write the string “Default constructor called” to the console, as shown in bold type in the following code example. The Point class should now look like this:

17.class Point

18.{

19. public Point()

20. {

21. Console.WriteLine("Default constructor called");

22. }

23.

24. public Point(int x, int y)

25. {

26. Console.WriteLine("x:{0}, y:{1}", x, y);

27. }

}

28.On the BUILD menu, click Build Solution.

The program should now build successfully.

29.In the Program.cs file, edit the body of the doWork method. Declare a variable called bottomRight of type Point, and initialize it to a new Point object by using the constructor with two arguments, as shown in bold type in the following code. Supply the values 1366 and 768, representing the coordinates at the lower-right corner of the screen based on the resolution 1366 × 768 (a common resolution for many Windows 8 tablet devices). The doWork method should now look like this:

30.static void doWork()

31.{

32. Point origin = new Point();

33. Point bottomRight = new Point(1366, 768);

}

34.On the DEBUG menu, click Start Without Debugging.

The program builds and runs, displaying the following messages to the console:

image with no caption

35.Press the Enter key to end the program and return to Visual Studio 2012.

You will now add two int fields to the Point class to represent the x- and y-coordinates of a point, and you will modify the constructors to initialize these fields.

36.Edit the Point class in the Point.cs file, and add two private fields called x and y of type int, as shown in bold type in the following code. The Point class should now look like this:

37.class Point

38.{

39. private int x, y;

40.

41. public Point()

42. {

43. Console.WriteLine("default constructor called");

44. }

45.

46. public Point(int x, int y)

47. {

48. Console.WriteLine("x:{0}, y:{1}", x, y);

49. }

}

You will edit the second Point constructor to initialize the x and y fields to the values of the x and y parameters. There is a potential trap when you do this. If you are not careful, the constructor will look like this:

public Point(int x, int y) // Don't type this!

{

x = x;

y = y;

}

Although this code will compile, these statements appear to be ambiguous. How does the compiler know in the statement x = x; that the first x is the field and the second x is the parameter? The answer is that it doesn’t! A method parameter with the same name as a field hides the field for all statements in the method. All this code actually does is assign the parameters to themselves; it does not modify the fields at all. This is clearly not what you want.

The solution is to use the this keyword to qualify which variables are parameters and which are fields. Prefixing a variable with this means “the field in this object.”

50.Modify the Point constructor that takes two parameters, and replace the Console.WriteLine statement with the following code shown in bold type:

51.public Point(int x, int y)

52.{

53. this.x = x;

54. this.y = y;

}

55.Edit the default Point constructor to initialize the x and y fields to –1, as follows in bold type. Note that although there are no parameters to cause confusion, it is still good practice to qualify the field references with this:

56.public Point()

57.{

58. this.x = -1;

59. this.y = -1;

}

60.On the BUILD menu, click Build Solution. Confirm that the code compiles without errors or warnings. (You can run it, but it does not produce any output yet.)

Methods that belong to a class and that operate on the data belonging to a particular instance of a class are called instance methods. (You will learn about other types of methods later in this chapter.) In the following exercise, you will write an instance method for the Point class, calledDistanceTo, that calculates the distance between two points.

Write and call instance methods

1. In the Classes project in Visual Studio 2012, add the following public instance method called DistanceTo to the Point class after the constructors. The method accepts a single Point argument called other and returns a double.

The DistanceTo method should look like this:

class Point

{

...

public double DistanceTo(Point other)

{

}

}

In the following steps, you will add code to the body of the DistanceTo instance method to calculate and return the distance between the Point object being used to make the call and the Point object passed as a parameter. To do this, you must calculate the difference between the x-coordinates and the y-coordinates.

2. In the DistanceTo method, declare a local int variable called xDiff, and initialize it with the difference between this.x and other.x, as shown below in bold type:

3. public double DistanceTo(Point other)

4. {

5. int xDiff = this.x - other.x;

}

6. Declare another local int variable called yDiff, and initialize it with the difference between this.y and other.y, as shown here in bold type:

7. public double DistanceTo(Point other)

8. {

9. int xDiff = this.x - other.x;

10. int yDiff = this.y - other.y;

}

NOTE

Although the x and y fields are private, they can still be accessed by other instances of the same class. It is important to understand that the term private operates at the class level and not at the object level; two objects that are instances of the same class can access each other’s private data, but objects that are instances of another class cannot.

To calculate the distance, you can use Pythagoras theorem and calculate the square root of the sum of the square of xDiff and the square of yDiff. The System.Math class provides the Sqrt method that you can use to calculate square roots.

11.Declare a variable called distance of type double and use it to hold the result of the calculation just described.

12.public double DistanceTo(Point other)

13.{

14. int xDiff = this.x - other.x;

15. int yDiff = this.y - other.y;

16. double distance = Math.Sqrt((xDiff * xDiff) + (yDiff * yDiff));

}

17.Add the return statement to the end of the DistanceTo method and return the value in the distance variable:

18.public double DistanceTo(Point other)

19.{

20. int xDiff = this.x - other.x;

21. int yDiff = this.y - other.y;

22. double distance = Math.Sqrt((xDiff * xDiff) + (yDiff * yDiff));

23. return distance;

}

You will now test the DistanceTo method.

24.Return to the doWork method in the Program class. After the statements that declare and initialize the origin and bottomRight Point variables, declare a variable called distance of type double. Initialize this double variable with the result obtained when you call the DistanceTo method on the origin object, passing the bottomRight object to it as an argument.

The doWork method should now look like this:

static void doWork()

{

Point origin = new Point();

Point bottomRight = new Point(1366, 768);

double distance = origin.DistanceTo(bottomRight);

}

NOTE

Microsoft IntelliSense should display the DistanceTo method when you type the period character after origin.

25.Add to the doWork method another statement that writes the value of the distance variable to the console by using the Console.WriteLine method.

The completed doWork method should look like this:

static void doWork()

{

Point origin = new Point();

Point bottomRight = new Point(1366, 768);

double distance = origin.DistanceTo(bottomRight);

Console.WriteLine("Distance is: {0}", distance);

}

26.On the DEBUG menu, click Start Without Debugging.

27.Confirm that the value 1568.45465347265 is written to the console window, and then press Enter to close the application and return to Visual Studio 2012.

Understanding static Methods and Data

In the preceding exercise, you used the Sqrt method of the Math class. Similarly, when looking at the Circle class, you read the PI field of the Math class. If you think about it, the way in which you called the Sqrt method or read the PI field was slightly odd. You invoked the method or read the field on the class itself, not on an object of type Math. It is like trying to write Point.DistanceTo rather than origin.DistanceTo in the code you added in the preceding exercise. So what’s happening, and how does this work?

You will often find that not all methods naturally belong to an instance of a class; they are utility methods inasmuch as they provide a useful function that is independent of any specific class instance. The Sqrt method is just such an example. If Sqrt were an instance method of Math, you’d have to create a Math object to call Sqrt on:

Math m = new Math();

double d = m.Sqrt(42.24);

This would be cumbersome. The Math object would play no part in the calculation of the square root. All the input data that Sqrt needs is provided in the parameter list, and the result is passed back to the caller by using the method’s return value. Objects are not really needed here, so forcingSqrt into an instance straitjacket is just not a good idea.

NOTE

As well as containing the Sqrt method and the PI field, the Math class contains many other mathematical utility methods, such as Sin, Cos, Tan, and Log.

In C#, all methods must be declared inside a class. However, if you declare a method or a field as static, you can call the method or access the field by using the name of the class. No instance is required. This is how the Sqrt method of the Math class is declared:

class Math

{

public static double Sqrt(double d)

{

...

}

...

}

A static method does not depend on an instance of the class, and it cannot access any instance fields or instance methods defined in the class; it can use only fields and other methods that are marked as static.

Creating a Shared Field

Defining a field as static enables you to create a single instance of a field that is shared among all objects created from a single class. (Nonstatic fields are local to each instance of an object.) In the following example, the static field NumCircles in the Circle class is incremented by the Circleconstructor every time a new Circle object is created:

class Circle

{

private int radius;

public static int NumCircles = 0;

public Circle() // default constructor

{

radius = 0;

NumCircles++;

}

public Circle(int initialRadius) // overloaded constructor

{

radius = initialRadius;

NumCircles++;

}

}

All Circle objects share the same instance of the NumCircles field, so the statement NumCircles++; increments the same data every time a new instance is created. Notice that you cannot prefix NumCircles with the this keyword, as NumCircles does not belong to a specific object.

You can access the NumCircles field from outside of the class by specifying the Circle class rather than a Circle object. For example:

Console.WriteLine("Number of Circle objects: {0}", Circle.NumCircles);

NOTE

Keep in mind that static methods are also called class methods. However, static fields aren’t usually called class fields; they’re just called static fields (or sometimes static variables).

Creating a static Field by Using the const Keyword

By prefixing the field with the const keyword, you can declare that a field is static but that its value can never change. The keyword const is short for constant. A const field does not use the static keyword in its declaration but is nevertheless static. However, for reasons that are beyond the scope of this book, you can declare a field as const only when the field is a numeric type such as int or double, a string, or an enumeration (you will learn about enumerations in Chapter 9). For example, here’s how the Math class declares PI as a const field:

class Math

{

...

public const double PI = 3.14159265358979323846;

}

Understanding static Classes

Another feature of the C# language is the ability to declare a class as static. A static class can contain only static members. (All objects that you create using the class share a single copy of these members.) The purpose of a static class is purely to act as a holder of utility methods and fields. A static class cannot contain any instance data or methods, and it does not make sense to try to create an object from a static class by using the new operator. In fact, you can’t actually create an instance of an object using a static class by using new even if you want to. (The compiler will report an error if you try.) If you need to perform any initialization, a static class can have a default constructor as long as it is also declared as static. Any other types of constructor are illegal and will be reported as such by the compiler.

If you were defining your own version of the Math class, one containing only static members, it could look like this:

public static class Math

{

public static double Sin(double x) {...}

public static double Cos(double x) {...}

public static double Sqrt(double x) {...}

...

}

NOTE

The real Math class is not defined this way because it actually does have some instance methods.

In the final exercise in this chapter, you will add a private static field to the Point class and initialize the field to 0. You will increment this count in both constructors. Finally, you will write a public static method to return the value of this private static field. With this field, you can find out how many Point objects you have created.

Write static members, and call static methods

1. Using Visual Studio 2012, display the Point class in the Code and Text Editor window.

2. Add a private static field called objectCount of type int to the Point class immediately before the constructors. Initialize it to 0 as you declare it, like this:

3. class Point

4. {

5. ...

6. private static int objectCount = 0;

7. ...

}

NOTE

You can write the keywords private and static in any order when you declare a field such as objectCount. However, the preferred order is private first, static second.

8. Add a statement to both Point constructors to increment the objectCount field, as shown in bold type in the following code example.

The Point class should now look like this:

class Point

{

private int x, y;

private static int objectCount = 0;

public Point()

{

this.x = -1;

this.y = -1;

objectCount++;

}

public Point(int x, int y)

{

this.x = x;

this.y = y;

objectCount++;

}

public double DistanceTo(Point other)

{

int xDiff = this.x - other.x;

int yDiff = this.y - other.y;

return Math.Sqrt((xDiff * xDiff) + (yDiff * yDiff));

}

}

Each time an object is created, its constructor is called. As long as you increment the objectCount in each constructor (including the default constructor), objectCount will hold the number of objects created so far. This strategy works only because objectCount is a shared static field. IfobjectCount were an instance field, each object would have its own personal objectCount field that would be set to 1.

The question now is this: How can users of the Point class find out how many Point objects have been created? At the moment, the objectCount field is private and not available outside the class. A poor solution would be to make the objectCount field publicly accessible. This strategy would break the encapsulation of the class, and you would then have no guarantee that its value was correct because anyone could change the value in the field. A much better idea is to provide a public static method that returns the value of the objectCount field. This is what you will do now.

9. Add a public static method to the Point class called ObjectCount that returns an int but does not take any parameters. In this method, return the value of the objectCount field, as follows in bold type:

10.class Point

11.{

12. ...

13. public static int ObjectCount()

14. {

15. return objectCount;

16. }

}

17.Display the Program class in the Code and Text Editor window. Add a statement to the doWork method to write the value returned from the ObjectCount method of the Point class to the screen, as shown in bold type in the following code example:

18.static void doWork()

19.{

20. Point origin = new Point();

21. Point bottomRight = new Point(600, 800);

22. double distance = origin.distanceTo(bottomRight);

23. Console.WriteLine("Distance is: {0}", distance);

24. Console.WriteLine("Number of Point objects: {0}", Point.ObjectCount());

}

The ObjectCount method is called by referencing Point, the name of the class, and not the name of a Point variable (such as origin or bottomRight). Because two Point objects have been created by the time ObjectCount is called, the method should return the value 2.

25.On the DEBUG menu, click Start Without Debugging.

Confirm that the message “Number of Point objects: 2” is written to the console window (after the message displaying the value of the distance variable).

26.Press Enter to finish the program and return to Visual Studio 2012.

Anonymous Classes

An anonymous class is a class that does not have a name. This sounds rather strange but is actually quite handy in some situations that you will see later in this book, especially when using query expressions. (You learn about query expressions in Chapter 21) For the time being, just accept the fact that they are useful.

You create an anonymous class simply by using the new keyword and a pair of braces defining the fields and values that you want the class to contain, like this:

myAnonymousObject = new { Name = "John", Age = 47 };

This class contains two public fields called Name (initialized to the string “John”) and Age (initialized to the integer 47). The compiler infers the types of the fields from the types of the data you specify to initialize them.

When you define an anonymous class, the compiler generates its own name for the class, but it won’t tell you what it is. Anonymous classes therefore raise a potentially interesting conundrum: if you don’t know the name of the class, how can you create an object of the appropriate type and assign an instance of the class to it? In the code example shown earlier, what should the type of the variable myAnonymousObject be? The answer is that you don’t know—that is the point of anonymous classes! However, this is not a problem if you declare myAnonymousObject as an implicitly typed variable by using the var keyword, like this:

var myAnonymousObject = new { Name = "John", Age = 47 };

Remember that the var keyword causes the compiler to create a variable of the same type as the expression used to initialize it. In this case, the type of the expression is whatever name the compiler happens to generate for the anonymous class.

You can access the fields in the object by using the familiar dot notation, like this:

Console.WriteLine("Name: {0} Age: {1}", myAnonymousObject.Name, myAnonymousObject.Age};

You can even create other instances of the same anonymous class but with different values:

var anotherAnonymousObject = new { Name = "Diana", Age = 46 };

The C# compiler uses the names, types, number, and order of the fields to determine whether two instances of an anonymous class have the same type. In this case, the variables myAnonymousObject and anotherAnonymousObject have the same number of fields, with the same name and type, in the same order, so both variables are instances of the same anonymous class. This means that you can perform assignment statements such as this:

anotherAnonymousObject = myAnonymousObject;

NOTE

Be warned that this assignment statement might not accomplish what you expect. You’ll learn more about assigning object variables in Chapter 8.

There are quite a lot of restrictions on the contents of an anonymous class. For example, anonymous classes can contain only public fields, the fields must all be initialized, they cannot be static, and you cannot define any methods for them. You will use anonymous classes periodically throughout this book and learn more about them as you do so.

Summary

In this chapter, you saw how to define new classes. You learned that by default the fields and methods of a class are private and inaccessible to code outside of the class, but you can use the public keyword to expose fields and methods to the outside world. You saw how to use the newkeyword to create a new instance of a class and how to define constructors that can initialize class instances. Finally, you saw how to implement static fields and methods to provide data and operations that are independent of any specific instance of a class.

§ If you want to continue to the next chapter

Keep Visual Studio 2012 running, and turn to Chapter 8.

§ If you want to exit Visual Studio 2012 now

On the FILE menu, click Exit. If you see a Save dialog box, click Yes and save the project.

Chapter 7 Quick Reference

To

Do this

Declare a class

Write the keyword class, followed by the name of the class, followed by an opening and closing brace. The methods and fields of the class are declared between the opening and closing braces. For example:

class Point

{

...

}

Declare a constructor

Write a method whose name is the same as the name of the class and that has no return type (not even void). For example:

class Point

{

public Point(int x, int y)

{

...

}

}

Call a constructor

Use the new keyword, and specify the constructor with an appropriate set of parameters. For example:

Point origin = new Point(0, 0);

Declare a static method

Write the keyword static before the declaration of the method. For example:

class Point

{

public static int ObjectCount()

{

...

}

}

Call a static method

Write the name of the class, followed by a period, followed by the name of the method. For example:

int pointsCreatedSoFar = Point.ObjectCount();

Declare a static field

Use the keyword static before the type of the field. For example:

class Point

{

...

private static int objectCount;

}

Declare a const field

Write the keyword const before the declaration of the field, and omit the static keyword. For example:

class Math

{

...

public const double PI = ...;

}

Access a static field

Write the name of the class, followed by a period, followed by the name of the static field. For example:

double area = Math.PI * radius * radius;