Chapter 5. Object-Oriented Programming in C#

IN THIS CHAPTER

Object-oriented programming is a fundamental part of the .NET Framework. The entire framework is built in an object-oriented fashion. Whether you are creating a game, a Windows Forms application, a Web Forms application, a web service, or anything else—if you’re using .NET, you’re using objects and classes.

Before you move through the rest of the book learning about Windows Forms and Web Forms, you should spend some time familiarizing yourself with object-oriented design (OOD) and object-oriented programming (OOP) concepts, as well as how to implement classes, inheritance, and interfaces in C#.

This chapter provides you with an introduction to the design and implementation of classes, interfaces, inheritance, and polymorphism.

Object-Oriented Design

The technical details of using C# to create classes and interfaces are fairly easy to master. The difficult thing to master is good design of those objects. This section gives you an introduction to object-oriented design (OOD), class design, and interface design.

Introduction to Object-Oriented Design

Object-oriented design isn’t just about designing objects— it’s about designing objects properly. When you are designing objects, two distinct areas of thinking are involved. You can either design an object that performs a specific, discrete task that is done when creating tools, or you can design an object that is a model or representation of a concept within the problem domain of the application. For example, you can create a class called CustomerManager, which is a tool-type object, or you can create a class called Customer, which models the real-world attributes and behaviors associated with a customer. The reality is that a little of each pattern is often used in commercial applications. This section illustrates some of the principles and concepts associated with designing classes and interfaces.

Designing Classes

Before the advent of object-oriented programming environments and languages such as C++, Java, and the .NET Framework, programming was procedural. In other words, your program started at some fixed point, and then invoked a series of subroutines in sequence to produce the desired result. More robust programs could be written by adding logic, looping, and conditional operators to the procedural code, but the code never actually modeled any real-world scenarios.

Classes are abstractions of real-world concepts used to allow developers to think in terms of the problem at hand rather than having to translate the problem into a series of convoluted procedures.

A class has attributes, which are essentially discrete pieces of information about the class. An attribute can either affect the class itself (referred to as a static member in C#) or it can affect instances of the class (instance members). An example of an attribute would be a field or a property of a class, such as ID, or members that are specific to a certain problem domain, such as Density, Volume, Price, or Age.

A class also has operations (referred to as “methods” in C#), which are the tasks that a class (or an instance of that class) can perform. These operations can range from simple constructors for initializing data, to complex operations that can consume valuable resources such as CPU time, memory, and so on.

When you design a class, you decide which attributes and operations the class will provide, as well as the accessibility of those attributes and operations. Accessibility refers to whether the class member can be accessed by other class members, by members of descendant classes, by code outside the class definition, or by code outside the assembly in which the class is defined.

This chapter does not discuss what constitutes good class design. The purpose of this chapter is to give you the knowledge you need to start writing object-oriented code with C#. If you want to do some further reading about object-oriented design, you should consult Object-Oriented Analysis and Design with Applications (2nd edition), ISBN 0805353402, Addison-Wesley Professional.

Visual Studio 2005 includes a tool that allows you to do class design within the IDE. You can create class diagrams from classes that you have already coded, and you can also generate classes from the diagrams you have drawn.

Figure 5.1 illustrates a class design in progress. It is an incomplete design of an object hierarchy representing vehicles. An object hierarchy like this could be used for various kinds of business problems—anything from selling cars to performing intense analysis of crash test results.

Figure 5.1 A work-in-progress class design.

Image

In the next section of this chapter, you will see how to take your class designs and turn them into code written in C#.

Designing Interfaces

The difference between an interface and a class is one that might not be immediately obvious if you’re new to object-oriented programming. A class is an encapsulation of attributes and operations that may or may not inherit from another base class (the CLR does not support multiple inheritance). An interface is a contract that defines the attributes and operations that a class must implement in order to satisfy the conditions of the interface.

For example, you can have a class that exposes a property called Color that might indicate the color of the object. You can also create an interface called IColorable. Any class that then implements that interface must define a property called Color. So, the essential difference between classes and interfaces is that classes are the definitions of abstractions of real-world entities, and interfaces are requirements and constraints to which all classes bound to the interfaces must conform. You will see more of how this works in the next section, where we get into some real object-oriented code.

Object-Oriented Programming

As stated in the preceding section, it is beyond the scope of this chapter to teach you all there is to know about good class design and OOD. The goal of this chapter is to show you how to implement your class hierarchies in C#. This section shows you how to create simple classes, how to implement the appropriate member security, how to create an inheritance tree, how to use polymorphism, and finally how to implement interfaces.

Creating Simple Classes

All classes begin with the basic class declaration, which defines the name of the class, as shown in the following snippet:

public class Vehicle
{
}

This is the most basic class definition. It will create a new class named Vehicle with no members and no methods. The class will be visible and available to be instantiated by any other .NET code because of the use of the public keyword.

Within the class definition itself, you can declare members that belong to the class itself, such as private members for storing internal data, properties, or methods. If you are following along inside Visual Studio, add a few member declarations for storing private data, as shown in the following example:

public class Vehicle
{
      private int numWheels;
      private int mpg;
}

Now you’ve got two members: one for storing the number of wheels on the vehicle, and one for storing the miles-per-gallon (mpg) rating of the vehicle. You’ll see how to handle member visibility (private, public, and so on) in the next section.

Next, let’s add an operation, or method, to the class:

Image

This method will display the number of wheels on the vehicle when the method is invoked. You invoke this method on an object instance as shown in the following example:

myVehicle.PrintWheels();

There are also special types of methods called Constructors, which are automatically invoked when the class is instantiated. For example, you can create a class that will automatically set the number of wheels to four whenever an instance of that class is created by using the following constructor:

public Vehicle()
{
       numWheels = 4;
}

This will set the private member numWheels to 4 whenever an instance of the Vehicle class is instantiated.

Finally, you can add some properties that allow code to access the private members numWheels and mpg, as shown in the following example:

Image

This is nowhere near a complete coverage of class building in C#. You can learn quite a bit about classes and object-oriented programming within C# simply by reading through this book.

Handling Member Visibility

C# enables you to control the visibility and access of classes and their members. You can prevent code from another assembly from seeing your code, or you can even limit the visibility so that only descendants (you will learn more about inheritance in the next subsection, “Using Object Inheritance”) can access specific members. Also, new to C#, you can individually control the accessibility level of the get and set accessors of individual class properties.

You control the accessibility of members through accessibility keywords, such as public, private, and so on. Table 5.1 gives you a complete listing of accessibility keywords and their meanings, as well as the types of code entities to which the keywords apply.

Table 5.1 Member Visibility Levels

Image

The following lines of code are examples of the various ways in which accessibility keywords can be applied to classes and individual members:

Image

With the table of accessibility levels in hand, the preceding code should make perfect sense. One thing to note, however, is the use of the keyword protected on the public property SomeNumber. This is a new feature in C# 2.0, and it allows you to differentiate the accessibility of one property accessor from another. For instance, in the preceding example, all code is allowed to retrieve the value of SomeNumber, but only derivative classes (inside or outside the assembly) are allowed to use the property to modify the value. This allows finer-grained control over what other code (possibly even code you or your team didn’t write) can do with your classes.

Using Object Inheritance

Object inheritance is the concept that allows one class to inherit attributes and operations from a parent class. In the case of the .NET Framework, a class can only have one parent. This is referred to as single inheritance. Languages such as C++ allow for multiple inheritance, which allows a single child class to inherit from multiple parents at the same time.

Inheritance is an extremely useful and powerful concept within the .NET Framework and within C# itself. You see inheritance used over and over in the base classes provided with the .NET Framework. For example, in Windows Forms, a ListBox control inherits from the ListControl class, which in turn inherits from the Control class, which in turn inherits from the Component class, which in turn inherits from the MarshalByRefObject class, which in turn inherits from the class from which all classes in the .NET Framework are derived, System.Object.

By allowing for rich object hierarchies, developers can realistically model business, data, and user interface scenarios in a truly object-oriented fashion, giving them rapid development ability and high code reuse.

The code in Listing 5.1 illustrates the use of inheritance and how the different members of classes in an object hierarchy are handled. There are two keywords that you need to familiarize yourself with when dealing with inheritance:

  • new—This keyword indicates to the compiler that the member you are defining on a derived class provides a new, alternate implementation than the member defined on the base class.

  • override—This keyword also indicates to the compiler that the member being defined provides a new implementation. The difference between override and new is that override no longer allows access to the inherited implementation. This difference will become clearer in the discussion on polymorphism.

Listing 5.1 The Animal, Cat, and Lion Classes Illustrating Object Inheritance

Image

Image

The first thing you see is an abstract class. An abstract class is essentially the concept of a class. It provides a standard set of code from which child classes can inherit. The difference between an abstract class and a regular parent class is that an abstract class cannot be instantiated. It is there purely to provide inheritable code. The Animal class sets up a default color for all child classes, as well as providing a default noise that child classes will inherit if they don’t modify it. The virtual keyword allows that method to be superseded by child classes if the creator of the child class so decides.

The Cat class inherits from the Animal class as indicated by the following line:

public class Cat : Animal

The Cat class sets its own color in the constructor, and provides its own implementation of MakeNoise without completely hiding the original parent’s implementation.

The Lion class inherits from the Cat class, providing its own color and its own implementation of MakeNoise. If we execute the following lines of code:

Cat c = new Cat();
c.MakeNoise();

Lion l = new Lion();
l.MakeNoise();

The output will be as follows:

Meow, already.
ROOooWWRrrrRR!!

The other thing to note is that all of the derivative classes have access to the members of the parent, so long as those members are protected or even more accessible.

Introduction to Polymorphism

Polymorphism is the ability to treat one object instance as if it were an instance of a different member of the inheritance tree. For example, using polymorphism and explicit typecasting, you can treat a Lion class as though it were a Cat class (because Lion inherits from Cat), and you can treat a Lion class as though it were an Animal class (because Lion inherits from Cat, which in turn inherits from Animal).

The real power of polymorphism comes from the fact that, through various keywords, you can control what members are used by which classes in the object hierarchy, even if the object is typecast to an ancestor. For example, we can create a class called ReallyBigLion that uses the override keyword that will provide its own implementation of MakeNoise(), even if the object is typecast to Lion instead of ReallyBigLion:

Image

Now, when we run the following code to demonstrate polymorphism:

Image

We will get the following output:

Meow, already.
REALLY BIG ROAR.
REALLY BIG ROAR.
Meow, already.

What’s interesting to note here is that when you typecast ReallyBigLion to Lion, the MakeNoise() method that is invoked is the one from ReallyBigLion because Lion used the virtual keyword and ReallyBigLion used the override keyword. However, when you typecast ReallyBigLion to Cat, the MakeNoise() implementation that is invoked belongs to the Cat class because the Cat class did not allow its method definition to be superseded with a keyword. This also illustrates how you can create a class and guarantee that no one will tamper with your implementation of a method if you don’t want them to.

Implementing Interfaces

As you read earlier in this chapter, interfaces are essentially contracts that define required attributes and operations for classes that implement those interfaces. Rather than being a hierarchical inheritance model, interfaces function more as a list of requirements to be enforced on a class. However, one interface can inherit from another interface, the end result being that the class that implements the child interface must adhere to all requirements as defined by both the child interface and the parent interface.

The following interface defines a list of requirements to which all implementing classes must conform:

Image

Note that there are no accessibility keywords. Interfaces do not define the accessibility of a member, only the member’s presence. The interface defined in the preceding example requires that implementing classes define both the get and set accessors for the NumLegs and Color properties, as well as a void method called MakeNoise().

Thankfully, Visual Studio makes it extremely easy to implement interfaces. To see this in action, create a new class file called Creature, and then type the following:

public class Creature : ICreature

After you type ICreature, you will be able to use the related smart tag to automatically implement empty stubs that implement the interface as defined. For big interfaces, this can save you a lot of typing and reduce a lot of typo-related errors. The resulting class is shown in Listing 5.2.

Listing 5.2 The Creature Class, Autofilled with Empty ICreature Implementation Stubs

Image

Image

With the empty stubs in place, all you have to do is remove the exceptions created by Visual Studio and replace them with real code, and your class will have satisfied the requirements of the interface.

Note that one of the basic reasons for an interface is that it provides one common type to which you can typecast a variety of object instances. For example, you might have 10 different object hierarchies that consist of Predators, Omnivores, Herbivores, and so on. However, all of these object hierarchies all implement the ICreature interface, so you can create methods that look like this:

Image

You can also typecast an object instance to any interface that the object implements with the C# typecast operator, as shown in the following example :

Image

You can also pass the object instance to a method that is expecting just the interface, and C# will do the type conversion for you:

SnowOwl owl = new SnowOwl();
DisplayCreature(owl);

The only time this will cause a problem is if you pass the method an object that doesn’t implement the required interface. In this case you will get a type conversion exception.

Summary

This chapter has provided you with some of the basics of how C# has implemented object-oriented programming. You saw how to create basic classes that expose fields, properties, and methods. In addition, you saw how to create object hierarchies using object inheritance, abstract classes, and polymorphism. Finally, this chapter provided a quick overview of creating and implementing interfaces. This chapter is far from the ultimate reference on object-oriented programming. However, it should give you enough basic information that you will be familiar with the OOP concepts and code used throughout the remainder of this book.

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

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