Learn - Today & Tomorrow!

Writing Object Oriented Software with C#

The .NET Framework is a new platform designed to target the Internet. The .NET framework is also an object-oriented platform from the ground up. And you most likely already know that C# is one of the most popular language choices for programming .NET or managed software. I love the ideas behind object oriented programming. The concepts are sound, and from time to time the implementations are just as sound. But the fact remains that many mainstream programming languages are less than pure in their approach to OO. C# is not one of these languages.

First things first, the API (application programmer interface) that C# uses to do stuff is called the Framework Class Library. The Framework Class Library is a huge library of objects meant for use in your managed code. C# and managed code in general really has only one way to gain access to underlying features of its host OS and that is through objects.

Second, C# is a first class citizen in the discipline of creating objects. C# comes complete with the basics, such as the ability to create classes of objects, abstract data,implement polymorphism and the like. C# extends these abilities with some nice bells and whistles such as properties,operator overloading, and built in event-handling constructs.

Third, the marriage of points one and two are surprisingly successful. The objects in the FCL are designed to be extended, and C# is a great language to use to make these derivations. The more I develop in C#, the more I notice that my applications are largely made of objects that extend objects in the FCL; meanwhile my application-only code is becoming increasingly terse. In fact much of my code fits-in so well with the objects in the FCL that they are hard to distinguish. This is a good progression and leads to more flexible software in general.

The remainder of this tutorial is almost completely dedicated to describing the features of C# as it pertains to object oriented programming. I am not going to attempt any major discussion on approach or design. But I do want to take a quick side-trip through some OO terms just to make sure that our terminology is in sync.

1 Object Oriented Concepts (A Refresher)

The basic goals of Object Oriented (OO) programming are simple. One is to hide complexity by abstracting data behind a wall of methods or functionality. The second (and this is related) is to group data with the code that manipulates the data. The third is modularity; the ability plug-in and un-plug code modules is veryimportant.

One means of reaching these goals is the creation of classes of objects. These classes of objects are often called classes, but for the remainder of this tutorial I am going to refer to classes of objects as types, and then when I address specific varieties of types I will use specific terms like class, structure, or enumerated type.

Another means of reaching the OO goals is through type derivation and polymorphism. These features help you as a programmer to realize the goal of code modularity.

The basic ideas are simple, but the implementations vary a lot, and so there is much to learn.

1.1 Types and Instances

As an object oriented programmer you will first and foremost create instances of types. This means that you will use a definition for a type, which has a name such as String or ArrayList, to create an actual object in memory. This object is structured based on the details described in the type’s definition.

After you have created an object, you can use it by calling methods and/or referencing fields on the object. When you are finished with the object it must be cleaned up. In some environments you do this explicitly; in others, such as C# or Java, cleanup is done for you by the system.

Creating instances is a nice introduction to OO programming, but eventually you will have to define your own type. By doing so you create a new classification for a kind of object that can be created. You give the type a name, and you create members of the type such as methods and fields.

It is important to distinguish between types and instances, so I will make an analogy. Think of the type as a descriptive tool (like a cookie cutter), while an instance is an object created from that description (in the same way that a cookie iscreated from a cookie cutter).

Figure 1‑1 Types and Instances

Finally, the process of using a type to create an object is called instantiation. In C#, C++, and Java the new keyword is used to instantiate an object. Sometimes I will use the conversational term, “new-up” to indicate an instantiation.

1.2 Type Derivation

Most object oriented languages allow you to derive a type from another existing type. (In fact C# and Java both require a type to be derived from a base type). When a type is derived from another type it becomes an extension of the basetype. As an extension it inherits with it all of the features and functionality of the base type, and most likely has some new features added.

If type Automobile is derived from type Machine, then type Machine is the base class or base type in the relationship. Conversely, type Automobile would be the derived type, or derived class. Because of derivation, types exist in a logical derivation hierarchy.

Figure 1‑2 Derivation

1.3 References to Instances

From the computer’s point of view, objects are data. They are the culmination of their fields and enough information to indicate their type. Often this data is complex and sizeable, and it is stored in the memoryheap of the program that created the instance.

Because objects so often live in the heap-memory of a program, the most common way of dealing with an instance is through a reference variable. The reference variable can be a global or local variable, or it can be a field in another object. Either way, there are some rules of reference variables.

Reference variables have a type associated with them. For every object-type defined in an object oriented system, there is a matching reference variable type that is used torefer to instances of the type.

Reference variables can refer to an instance or object, or they can refer to null (in most OO languages anyway). A null reference simply indicates that this variable does not refer toany object, but could have an object reference assigned to it.

Reference variables do not always refer to objects of the exact sametype as the reference variable. This can be confusing, but in fact the rules are simple. A reference variable must refer to an object of matching type or it must refer to an object that is ultimately derived from the matchingtype.

Looking back to the relationship between Automobile and Machine, a reference variable of type Automobile can only refer to an instance of Automobile; however a reference variable of type Machine can refer to an instance of Machine or an instance of Automobile. You can look at this as being possible, because Automobile is a Machine through the affect of derivation.

Reference variables are your means of access to an object or instance. What this means is that you use the reference variable to touch fields of the object and you use thereference variable to call methods on the object. There are two related rules of reference variables that make the most sense when stated together.

Regardless of what type of instance your reference variable refers to, it is the type ofthe variable that constrains how you can touch or affect the instance.

Regardless of what type of reference variable you are using to refer to an object, thetype of the object never changes for the life of the object.

1.4 Polymorphism

Polymorphism is closely related to type-derivation and reference variables. But it is an advanced topic, and can be difficult to describe. The goal is to allow a generic piece of code to work with objects or instancesgenerically; meanwhile we want the objects themselves to do the right thing based on their respective types.

An example of this would be a piece of code that iterates over an array of references to Automobile objects. The twist, however, comes in the fact that the various instances areactually derived types such as Car, Motorcycle, and Bus.

If part of the algorithm was to call an instance method called GetNumWheels(), you would want to be sure that the GetNumWheels() method for a Car is called when the instance is a Car, and the GetNumWheels() method for a Motorcycle is called when the instance is a Motorcycle.

This is not what happens by default, however, since by default the system sees your code calling the GetNumWheels() method when your reference variable is an Automobile reference variable. The natural flow of code would be to call the code for GetNumWheels() implemented by the Automobile type.

To get the polymorphic behavior that we want we must mark the GetNumWheels() method as virtual on the base class, and all of the derived classes must override the virtual function.

1.5 Members and Basic Accessibility

When defining a type you will give it a name, and perhaps a base type. This is the easy part. You still need to make the type do something. To do this you must define typemembers.

Type members come in two basic forms. These are fields and methods. Fields are the data that make up an instance of your type. Methods are the functions defined by your type to manipulate the data (although not all types have fields and not allmethods manipulate data). As you work with OO languages and platforms you will see many variations on the method andfield rule, but ultimately types are defined as fields and methods.

Most object oriented languages allow you to define more than one method (for a single type) with the same name. This is called method overloading, and it is a requirement that the parameters of the methods differ so that thecompiler has enough context to know which of the various overloads of a method is being used in a specific piece of code. The composite of a methods name and parameter list is called a methods signature.

Members must have a defined accessibility. The accessibility of a member (be it field or method) defines who can access the member. Here are the basic accessibilities defined by most OO languages.

Public – If you hold a reference to an object, you can access all of its public members.

Private – Only the code in the member functions of a type can access the private members of that type. For many OO languages, private is the default accessibility, since the goal of OO is to hide data.

Protected – Only the code in the member functions of a type or a type derived from thetype can access protected members of the base type.

2 Designing Types

Ok, we have talked a little bit about terms, now it is time to talk about designing types with C#. There are actually a couple of different kinds of types that you can define with C#, andeach has its own special behavior. The class is the most common type, so we will start with that.

To define a class you use the following syntax.

class Name:BaseType
{
}

The keyword class is required. The Name indicates the name of your class, and the BaseType indicates the base class for your new class. In C#, or any managed language, all types must have a base class. If one is not defined, then the base class for your type is implicity the Object class defined in the Framework Class Library (FCL).

Types defined for use only by your application will often exist in the default namespace, which is to say that the type has no namespace. However, as you design types for reuse, you may want to place the type in a namespace declaration as follows.

Namespace NameName
{
class Name:BaseType
{
}
}

In this case a type named NameName.Name is defined. You can place more than one type definition inside of a namespacedefinition, and you can reuse namespaces across code modules and assemblies.

Normally you will want your type to do something (other than what the base type already does), so you will add members. Members come in the form of fields and methods.

2.1 Fields

Fields in a type are data. In C# fields come in two flavors, static and instance. Instance fields are the more common, and are part of the definition of an object when it is created. Instance fields are object data.

Static fields, on the other hand, are data associated with the type. In a way you can think of static fields as data that are shared between every instance of a type.

In the preceding code example, you can see a type named MyType that defines three fields. Two of the fields are instance fields of type Int32, and the third is a static field of type String.

If you instantiate an instance of MyType, the new instance gets its own copy of the x and y fields. However, as each new instance of MyType gets its own instance fields, there remains only one field named someTypeState which is shared globally amongst each instance.

Notice that each field in the example is explicitly marked as public. This means that all code has access to the fields of MyType. If the fields had been marked as private, then only methods of MyType (of which there are none) could directly access the fields in the class. This is how that definition would look.

And since the private accessibility is the default in C#, the following definition compiles to the exact same binary as the preceding code.

class MyType
{
static String someTypeState;
Int32 x;
Int32 y;
}

Note: The .NET Framework also has a concept of readonly fields which can only be written to by code inside of a constructor method.

2.2 Methods

In object oriented programming, it is very common for all fields of a type to be private, and for the only access to a type to be obtained through public or protected method calls.

Like fields, methods come in two flavors, static and instance. Instance methods refer to an object, and must be called through a reference variable that refers to an object instance. Static methods are often called type-methods and can be called without an instance of the type. The syntax for calling a static method is.

The following type definition shows how to define a class with several methods and some fields.

In the preceding example two instance methods GetX() and GetY() are defined as accessor methods for the private fields x and y. Finally, a static method is defined, and in this case, the static method returns an instance of the type (this is a common design patterncalled a factory method).

In C# programming methods are used to access data in instances of types. Methods are also used just to get an application task done. It is common in C# to define types with nothing but static methods (and no data), just as a logical collection of methods that share a common idea or goal, if not common data. (The Console type defined by the FCL is an example of such a type).

2.3 Properties

Properties are a great addition to object oriented languages. Properties are often called smart fields, because the can be accessed using field access syntax, but under the covers they are really methodcalls.

The advantage of properties is simply that your actual data fields can remain private, while code that consumes your types can consume the fieldsthrough properties. This allows your type to validate data, or do whatever needs to be done in the method portion ofthe property.

A property has a name and a type, and comes with a read function and a write function named get and set respectively.

In Figure 2‑1 the simple Pointclass uses properties to elegantly allow access to the private fields x and y.

Note: The get and set methods are like any other method, and anything can be done in them (however, since these ones are so short, I implemented them on a single line).

Notice that the code in Main() method creates an instance of Point, and then calls both the get and the set property methods. However, it looks like a simple field access.

Note: It is possible to define read-only and write-only properties by just implementing one or theother of the accessor methods. It is not valid to define a property with no accessor methods.

You should use a property wherever you are tempted to define a public field. You should do this even if you are doing what I am doing in the Point.cs Property Example. The reason is that you may some-day want to add validation. Or you may want to add an event that is fired when a value is changed. If you are using a public field, then you can not do either of these things, but withproperties you can.

If you change a member field to a property, all code that references your type will have to be compiled.

2.4 Constructors

When an object is created or instantiated, the .NET Framework allocates memory for the fields of the object, as well as for any overhead data necessaryto maintain the object’s type identity. Although the Common Language Runtime automatically initializes the overheaddata, the field data’s initial values must be set.

To do this, you can define a special method for your type called an instance constructor. The constructor method is called by the runtime when an instance is created. It is possible for constructors to be overloaded so that an instance of your type can be created with a variety ofstarting data.

The C# compiler knows you are defining a constructor method when you give a method the exact same name as the type itself. For example, if we were to add a constructor for the Point type in Figure 2‑1 we would name the constructor Point.

Inside of your constructor methods you should initialize the fields of your type.

In this update of the Point type, there are two constructors that can be used to new-up an instance of Point. The first constructor takes no parameters. A destructor that takes no parameters is called the defaultconstructor. In this example the default constructor is actually implemented in-terms-of the more complex overload of the constructorwhich expects two parameters.

It is not necessary for constructors to be implemented in terms of each other, but it is strongly recommended, so that your initialization code needonly be written once.

You could create an instance of the Point type in Figure 2‑2 with either of the two following lines code.

Point p = new Point();
Point p = new Point(10, 10);

In the first case you would get an instance of Point with its x and y value initialized to 0, and in the second case you would get a Point object with its x and y fields initialized to 10.

You should try to keep your instance constructor code is minimal as possible, and generally it should relate only to the setting of fields in aninstance.

Constructors can be marked with any accessibility modifier, including private. A private constructor will cause your type’s methods to be the only code that can new up an instance ofit. This is a common design pattern for types where a static factory method is used to create instances.

2.5 Type Constructors

With C# you can create a constructor method designed to initialize a type, rather than to initialize an instance of a type. This can be useful for setting the values of static fields in your type.

Type constructors are often called static constructors, because you use the static keyword to indicate that a constructor method is a type constructor. Type constructors must also be named for the type, must take zero parameters, and cannot be overloaded.

The system guarantees that a type constructor for a type will be called before any other reference to a type or an instance of a type is reached. However, C# does not guarantee that a type constructor will be called at a deterministic time in your program’s execution.

2.6 Accessibility

Most object oriented languages provide accessibility modifiers for members such as public, private, and protected. The .NET Framework has support for several others, and so does C#.

Accessibility Modifier

Description

public

All code can access this member or type.

private

Only methods in the containing type can access this member.

protected

Only methods in the containing type or in a derived type can access this member.

internal

Methods in the same assembly can access this member or type.

protected internal

This member is accessible by code that would have had access to a protected type, and it is accessible to code that would have had access to an internal type.

Figure 2‑3 Accessibility Modifiers

Both the internal and protected internal accessibility modifiers are advanced and less commonly used with members (although the defaultaccessibility for a type is internal). For an introduction to assemblies see the tutorial named Introducing the .NET Framework with C#.

2.7 Derivation

We have already talked about derivation, and we will talk about it quite a bit more in the section on Polymorphism. Here I would just like to show a complete example of derivation.

The code in Figure 2‑4 shows a type NamedPoint that is derived from the Point type defined in Figure 2‑2. Even though the NamedPoint type defines only one field name and one property Name, an instance of NamedPoint will contain all three fields name, x, and y which is the composite of the NamedPoint and Point types.

Not only is it not necessary for NamedPoint to define its own fields x and y, but an instance of NamedPoint is both of type NamedPoint and of type Point, so a reference variable of either type can be used to reference an object of type NamedPoint.

Note: Because all types are ultimately derived from the Object type, reference variables of type Object are often used as generic references in managed code. The Object type also defines a method named GetType(), which is inherited by every managed object, and it can be called to obtain the type of an instance.

The constructor syntax for NamedPoint is also noteworthy. First, notice that the simpler constructor is implemented in terms of the more complex constructor. This is consistent with the code in Figure 2‑2. Second, notice that the implementing constructor indicates which versionof the base-types constructor should be used to construct the fields that are part of the base type.

2.8 Polymorphism

In C# polymorphism is implemented through the use of virtual functions in much the same manner that it is with C++. The syntax is a little different however.

When defining a type, if you are creating a method for which you want polymorphic behavior, then you should use the virtual keyword to decorate the method as follows.

In the derived type, if you chose to overload the virtual function you should create a method with the exact same name and signature and decorateit with the override keyword.

The code in Figure 2‑5 is a fairly complete example of derivation at work, including a healthy dose of polymorphism. The first two lines of the Main() method create an array of Object references, and then assigns references to five new objects to the array.

The line of code in red is the code that is calling an instance method on each object in the array. This instance method is the ToString() method defined by the Object type in the FCL. The ToString() method is virtual and will exhibit polymorphic behavior.

If you compile and run the code in Figure 2‑5 you will see that each instance prints its own name to the console window, because the appropriate version of ToString() is called on a per-instance bases.

Note: Four of five of the instances in this example are derived from the Citrus type. But one of the objects, the instance of Truck, is not. This is why our array must be an array of Object references rather than an array of Citrus references. If we remove the Truck object from the set, then we can upgrade the type of the array to Citrus to more closely represent the instances contained within.

3 Event Handling

The C# language has built-in support for event handling. Event handling is used for notifications such as when a mouse click has happened, or when a menu selection has beenmade. Although GUI applications make the most use of event handling, types can use events to allow notification of anykind.

3.1 Handling Events

Handling events is probably the simplest way of working with events. (There is a fair amount of infrastructure that is not clear until you implement an event, but I will getto that shortly).

The code in Figure 3‑1 shows a simple event registration. In the constructor of MyForm a Button instance is created and added to the form’s controls. Before this is done, MyForm registers its interest in the button’s Click event. This is done by adding a new instance of the EventHandler delegate to the Click event as shown in red. (A delegate is a managed way of encapsulating a callback function. We will discus delegates more shortly).

The handler function method is called HandleClick() and is shown in red as well. This method is called each time a user clicks on the button and causes the Click event to be fired.

Here are a couple of points about this code.

Notice that the += syntax is used to register your interest in an event. This is used because it is possible for more than one method to be registered and called when an event is fired. (To unregister a handler use the -= operator).

The parameter list and return type of the HandleClick() method must match the defined parameter list and return type for the EventHandler delegate. The EventHandler delegate is a type defined by the FCL.

The Click event is defined as a member of the Button type in the FCL. When you define events for your own type’s you may use any delegate you like for the callback method, or you can createyour own delegate. Remember that the delegate defines the signature of the callback method.

3.2 Defining Events

If you are creating a type that needs to allow code to register its interest in an event then you must decide what information you want to pass tothe handler methods when the event is raise. This will affect your choice of delegate type.

The EventInt.cs example is a simple (and most likely useless) type that shows the definition of an event. The EventInt type implements an Int32 value through a property, and the property fire’s a Changedevent if the value is changed. The firing code is shown in red.

The type defines its own delegate (which is a nested type) named Callback. The Callbackdelegate indicates that a handling method must return void and take two Int32 parameters.

The EventInt type also defines an event named Changed which is based on the Callbackdelegate. To register your interest in this event you would use the EventInt type in much the same way as the Button type was used in Figure 3‑1.

The code in Figure 3‑3 creates an instance of EventInt, and then registers a handler. Notice that the HandleChange() method matches the definition of the Callbackdelegate in Figure 3‑2. In this code example, the HandleChange() method is called twice.

3.3 Callback Methods

Events are very common in managed code, but sometimes you will only need to use a callback. In this case you can use a delegate, without an event. Delegates are special types that are defined with special syntax that looks almost like afunction definition.

public delegate void Callback(Int32 newVal, Int32 oldVal);

The preceding code defines a type named Callback that can be used to represent a method to be called back.

The interesting thing about delegates is that you can chain them. This means that you can register more than one callback and fire it with what looks like a single method call.

In this example a delegate named MyDelegate is defined, and two instances are created, one-each for the methods FirstMethod and SecondMethod. Then the delegate chain is called back three times with three different messages.

4 Interfaces

Interfaces are a great feature of object oriented programming. Some languages such as C++ support a concept called multiple derivation. The .NET Framework does not allow for multiple inheritance, and as a result neither doesC#. Sometimes, it is nice, however, for a type to be able to take on several roles. This is where Interfaces come in.

A type is what it is and it is also what its base class is. But in programming sometimes it is nice to have a well defined role that a type can fulfill, without the role beingdefined as a base class. Here is an example.

In the Figure 4‑1 a type SortType is derived from a type SomeType. Even though each instance of SortType continues to also be a SomeType, they have the additional functionality of having implemented the IComparable interface’s CompareTo() method.

It’s this interface that the Sort() method of Array looks for when trying to sort the array of objects. Inside of the array method, each object of the array is being cast to a reference variable of type IComparable, and then the CompareTo() method is being called, like so.

IComparable ref = (IComparable) obj;

This is how any object becomes an IComparable just long enough to compare itself (although the instance isn’t really changing at all, but the code’s view of the instancehas changed). Interfaces are a way for objects to have flexible roles while maintaining a strict type.

5 Operator Overloading and Type Conversion

Operator overloading and type conversion are features of C# that allow you to create types that behave like primitive types. What this means is that you can use the operators such as +, =, and * on your type and the system will call your special methods defined on your type.

5.1 Operator Overloading

Operator overloading in C# is very flexible. Normally when you create a new type you cannot use operators like + and – on instances of the type, because the compiler does not know what to do with the instance in that context. But, if you define a special static method on the type to indicate what the compiler should do, then the compiler willautomatically call your method when it sees the type being used with the matching operator.

In the preceding figure the MyType type implements two static methods to overload the + and | operators for use with instances of MyType. You can do whatever you like in an operator overload method, just like any other method.

When the compiler sees the code that attempts to add to instances of MyType it calls the method for the + overload. Likewise it calls the operator for the | overload when it sees the | used with instance of MyType.

Notice that the return value of an operator overload is flexible. In fact operator overload methods can be defined with any combination of parameters and return values so long as atleast one of the parameters is the same type as the containing type.

5.2 Casting Operators (type converters)

Type converters are similar to operator overload methods. Type converts allow you to assign an instance of one type to a variable of another type using an assignment operator such as =, or you can define a converter that allows you to cast to another type using the castsyntax (type).

In this example an instance of MyType is created using assignment syntax and the integer value 10. This is possible, because an implicit casting operator is defined for the MyType. The casting method is called at the point of assignment, and the method does what it needs to do to return aninstance of MyType.

Similarly, an instance of MyType is explicitly cast to an Int32. The compiler would normally produce an error, except that an explicit cast operator overload is defined for MyType which casts an instance of MyType to an Int32.

You can create as many explicit and implicit cast operator overloads for your type as you like. The only rule is that either the source or resulting type must be the same type as your type. The explicit overload will require casting syntax (type), while the implicit overload will make the conversion as needed without the cast syntax.

6 OOP in Practice

C# is a great language for using object oriented programming techniques in your day-to-day projects. C# has the functionality to allow you to create and derive objects in a way that will makeyour software more flexible.

The .NET Framework allows you to compose your applications of objects that were built before your application was compiled. This is similar to Java, but a major departure from C++. You will see that more and more applications are largely made up of pre-built components (often createdby third parties). Similarly, you will find that in your own code you will write more reusable objects, because theplatform is so conducive to this kind of programming.

Object oriented programming takes more than great languages and tools, however. If you are new to OOP then I suggest you seek as much exposure to the topic as possible, there is nosubstitute for experience.

Review the samples from this tutorial, and the other tutorials in the series. Almost any C# application will exhibit object oriented programming concepts. Enjoy C# and enjoy object oriented programming.

Banner

Latest posts

What is a Design Pattern?
According to MSDN, "A design pattern is a description of a set of interacting classes that provide a framework for a solution to a generalized problem in a specific context or environment. In other words, a pattern suggests a solution to a particular problem or issue in object-oriented software development.”

In this article, I will discuss how we can create a Line, Rectangle, ellipse and Polygon control in WPF at design-time using XAML and at run-time using C#.I will also explain their properties to format such as fill property. After that, i will show you to set an image as the background of any shape object.

One question is always out there in most interviews. What is the difference between an Abstract Class and an Interface and why do you use one over the other? It seems like a simple question but most often, the answer to this question is not sufficient for the interviewer and can cost a job. So, I have decided to write an article about them and have tried to explain it.