by kirupa |
5 January
2007
In the real world, you categorize and sub-categorize your
environment based on the objects that can be found in it.
For example, you have a chair which can be categorized as
furniture. Your refrigerator can be categorized as an
appliance. There are many such examples where a specific
object is classified by a more general category.
When
it comes to writing code, you can represent things as
objects also. This style of programming is known object oriented programming
(OOP) where we extend our idea
of objects from the real world and represent them using
code.
In this tutorial, I will provide you with an introduction to
OOP by covering classes in C# and some of the interesting
things to be on the lookout out for. Since this will be a
long (but fun!) tutorial, I have divided the content into
the following smaller sections for easier digestion:
- Introducing Classes and Objects
- Setting Up your Project
- Defining a Class
- Public and Private Modifiers
- Constructors
- Methods (Functions)
- Instance Methods
- Static Methods
- AutoComplete in Visual Studio
- Conclusion
Let's step away from the world of computers and think
about how objects and classes relate to the real world, and
since I am a big fan of outer space, I will use an
intergalactic theme throughout this tutorial.
In our solar system, you have planets. For something to
be a planet, it must meet the following criteria:
- Orbits around the sun.
- Has enough mass to maintain a stable shape.
- Does not shine with its own light.
There are a few more criteria, but you get the basic idea
of what characteristics something must possess in order to
be considered a planet. With your definition of planet set,
you can then associate real world things that would fall
under the planet category. As you know, there are quite a
number of planets in our solar system - Earth being a
particularly important one!
Ok, now let's link the planet example to our goal of
understanding classes. A class
would be similar to the definition of planets, for
classes set the guidelines and specify the
characteristics necessary for something to be associated
with it. The individual planets themselves - Mercury, Mars,
Earth, etc. - would be considered objects
or instances
because they inherit their unique characteristics from a
class called planet. Another way
of saying it would be that these objects are of
type planet.
To recap, in order for something to be a planet, it must
posses all the characteristics as defined by your Planet
category. In order for an object to be part of a class, the
object must incorporate all of the characteristics specified
as being associated with the class. I think you now have a good
idea of what classes and objects are. There are more
nitpicky details, but for now, we'll play it by ear and
learn those details along the way.
In this tutorial, you will be writing some code to put into
practice what you read. While you can use any IDE, I will
provide instructions for what most of you probably use for
your .NET development, Visual Studio 2005:
- Launch Visual Studio. Once launched, go to File |
New | Project:
- The New Project window should appear. From
this window, select Console Application under the
Templates view:
- Glance your eyes down a few inches and
enter ClassTutorial as the name for
your project:
- Press OK to accept the changes.
You now have a empty project with a Main method that you
can use to test your code in.
Let's now take a look at defining a class. Copy and paste
this code directly above your class
Program definition in the code view:
- class
Planet
- {
- public
int
radius;
- public
int
gravity;
- private
string
name;
- }
For clarification on where exactly to paste the code, my
code view looks like the following after pasting the above
code:
- using
System;
- using
System.Text;
-
- namespace
ClassTutorial
- {
- class
Planet
- {
- public
int
radius;
- public
int
gravity;
- private
string
name;
- }
-
- class
Program
- {
- static
void
Main(string[]
args)
- {
-
- }
- }
- }
Let's get back the Planet class you copied and pasted
earlier. In the first line I use the class keyword followed
by the name of our class. It is good programming convention
to capitalize the first letter of a class.
The body of our class contains three fields (variables):
radius, gravity, name. Any object that is based on the
planet class will have a copy of these three variables and
any values you assign to these variables.
If you wanted to use our Planet class, all you need to do
is create an object of type
Planet:
- Planet
earth
= new
Planet();
- earth.gravity
= 9.81;
- earth.radius
= 6378;
Notice that I use the new
keyword to initialize earth to refer to an object
of type Planet. With our earth object now created, we can
assign values to the variables radius and gravity. Notice
that when you attempt to assign a value to the name field,
you receive an error. The reason is that name is a
private field whereas radius and gravity
are both public fields.
A public field is one in which you can access the values
from outside of the class. For example, an object can access
something marked as public as you saw when we assigned
values to our gravity and radius fields.
When something is marked private, though, that means only
code stored within our class can access it. Our earth object
is beyond the reach of our private name field, so we
couldn't access the name field and assign it a name like we
may have wanted.
When you create an object, you use something called a
constructor. A constructor is usually a method (function)
whose name is same as that of our class, and it often takes
in arguments that help initialize any fields that the object
would need.
In our Planet class, we do not have a constructor. When
you do not create a constructor, a default constructor is
provided for you. That is why when you typed
Planet earth = new Planet(),
your compiler did not give you an error.
So, let's modify our Planet class by creating a
constructor:
- class
Planet
- {
- public
int
radius;
- public
double
gravity;
- private
string
name;
-
- // A Constructor
- public
Planet()
- {
- Console.WriteLine("Constructor
called!");
- }
- }
In the above code example, notice that I created a simple
constructor called Planet(). This constructor does pretty
much the same thing as our automatically generated default
constructor, but one difference is that in my version, I
output "Constructor called!" to the console everytime a new
Planet object is created.
A default constructor is not fun. After all, a primary
reason for using a constructor is so that your objects can
have their fields initialized when they. A default
constructor doesn't help with that unless you have no fields
that need initializing. Since we have three fields that need
to be initialized, let's modify our Planet constructor
again:
- class
Planet
- {
- public
int
radius;
- public
double
gravity;
- private
string
name;
-
- // A Constructor
- public
Planet(int
r,
int
g,
string
n)
- {
- radius
=
r;
- gravity
=
g;
- name
=
n;
- }
- }
Notice that our constructor takes in three arguments this
time. In the body of the constructor, I assign our
radius,
gravity, and
name fields the value of the
incoming r,
g, and
n arguments.
To use the above constructor, you will use the following
line:
- Planet
earth
= new
Planet(6378,
9.81,
"Earth");
Notice that our earlier no-argument
new Planet() constructor
will no longer work. Since your constructor now takes in
three arguments, you must provide the three arguments needed
or else you won't be able to run your program.
But notice that, since you pass in the values you want
your fields to have, you no longer need to initialize them
separately. If you want to know what values your earth
object's radius and gravity variables possess, all you have
to is type Console.WriteLine(earth.radius
+ ", " + earth.gravity);
Despite our constructor simplifying how fields are
initialized, we still cannot access our private name field
outside of the Planet class. Notice, though, that our
constructor was able to assign the value "Earth"
to our name field because the constructor is inside the
class itself. Hopefully before the next section is over, we
will figure out a way to access the data stored by our
name variable!
Up until now, our Planet class only had fields and a
constructor. They are great for simply creating your object
and initializing fields, but beyond that, they cannot help
you. In order to have your class do more interesting things,
you will need to use methods. You may see
methods referred to by name as
functions also, but in order to be consistent,
I will only be using the word method.
In a nutshell, a method is a block of code that does
something specific. It has a name and various tags
(properties) that describe its behavior. More importantly,
methods also have the ability to return data back to whoever
called it. For example, you probably have been using a
method called Main to test your programs:
- static
void
Main(string[]
args)
- {
- Planet
earth
=
new Planet(6378,
9.81,
"Earth");
-
- Console.WriteLine("Earth's
radius is: " +
earth.radius);
- }
To best describe the individual parts that make up a
method, I will use the diagram:
Your method declaration follows the template shown in the
above image. The following list explains what each part
means:
Access modifiers define how easily accessible your
method will be, and this will be very important when I
explain inheritance. The access modifiers we learned so
far are public and
private, and the other modifiers,
which I will cover on a later date, are
protected, static,
internal, readonly,
volatile, new,
and protected internal.
The type of an object is basically the class it belongs
to. The return-type is then the type of the data you are
returning when the method is called. If you set your
return type to void, that means that you are not sending
any data back. We'll see an example of this in use
shortly.
This is pretty easy! This is just the name you give your
method.
Your method can take in values much like how our
constructor took in values to assign values to our
fields.
Your method's code would go here.
Now that you have a basic idea of what goes into a
method, let's look at the first type of method I will cover,
Instance Methods.
Instance methods are methods you
call from a object. The best way to explain it is via an
example. For the past few pages, we have tried to get
data from our
name field to display when accessed via our object.
Let's use a method to do that right now.
Add the following non-grayed out code to your Planet
class:
- class Planet
- {
- public int radius;
- public double gravity;
- private string name;
-
- // A Constructor
- public Planet(int r, double g, string n)
- {
- radius = r;
- gravity = g;
- name = n;
- }
-
- //Return the object's
name!
- public
string
GetName()
- {
- return
name;
- }
- }
More specifically, the code you added is shown below:
- //Return the object's name!
- public
string
GetName()
- {
- return
name;
- }
Let's dissect our GetName() method. Notice that its
return type is string, and in the body of our method, we
return the name field which is also of type string. The
return type of your method must match the return type of the
value being returned. Finally, notice that the access
modifier of our method is public. Guess what that means?
You'll find out when you add the following code to your
Main method:
- static
void
Main(string[] args)
- {
- Planet
earth
=
new Planet(6378, 9.81,
"Earth");
-
- Console.WriteLine("Earth's
radius is: " +
earth.radius);
- Console.WriteLine("Earth's
name is: " +
earth.GetName());
- }
Notice that when you run your application by pressing
Ctrl + F5, you will see not only the radius being being
displayed, but the name is also displayed:
The reason the name is displayed is because while the
name field is private, the method that returns the value is
public. Our goal was to hide the name field, but not to hide
the GetName() method. Since GetName() is within our Planet
class definition, returning the private name field is
allowed.
The other type of method you can use is called a static
method. Unlike instance methods that could only be called
from an object (ie: earth.GetName()),
static methods can be called by polling the class directly.
Like before, let me first provide an example and then I
will discuss the details. Add the following colored lines to
your Planet definition:
- class
Planet
- {
- public
int
radius;
- public
double
gravity;
- private
string
name;
- private
static
int
count;
-
- // A Constructor
- public
Planet(int
r, double
g, string
n)
- {
- radius
=
r;
- gravity
=
g;
- name
=
n;
-
- count++;
- }
-
- //Return the object's name!
- public
string
GetName()
- {
- return
name;
- }
-
- //Static method returning
count of all planets
- public
static
int
GetCount()
- {
- return
count;
- }
- }
The above changes are fairly minor. I first declare a
static int field called count. In the constructor, I
increment our count field by one by using the ++ operator.
Second, I created a static method called GetCount() that
returns the value stored by our count field. What I have
just written still does not explain what static
represents, but if you wait a few more lines, you will find
out what it does.
To see what the code you added to the Planet class
accomplishes, overwrite your main method with what I have
provided below and run your program:
- class
Program
- {
- static
void
Main(string[]
args)
- {
- Planet
earth
=
new
Planet(6378,
9.81,
"Earth");
- Planet
saturn
=
new
Planet(60268,
8.96,
"Saturn");
-
- Console.WriteLine("Total
planets so far are: "
+
Planet.GetCount());
- }
- }
Notice that when you run your program, your output should
be "Total planets so far: 2" That is, after
all, the correct answer because we only have two planet
objects created - earth and saturn.
What is interesting is more interesting than the number
of planets is how I call the GetCount() method:
Planet.GetCount(). Instead
of accessing GetCount() from either the
earth or
saturn objects, I am going
straight to the Planet class
itself. That outcome is a result of tagging the GetCount()
method with the static modifier.
The following diagram should help you get an idea of how
static and instance methods/ and fields are shared in our
program:
As you can see from the dotted lines in the above image,
static variables and methods are shared
among all objects. Even though I have two objects called
earth and saturn, only one copy of the
count field and
getCount() method are
generated. If you remove the static modifier, you will only
be able to access the getCount() method via an object, for
example - earth.GetCount().
Also, the data will be localized to just your object as seen
by the instance variables and GetName() method in the blue
Saturn and Earth boxes.
Up until now, I gave you the impression that you had to know
beforehand which methods and fields were public or private
before attempting to access them. In Visual Studio, the
Auto-Complete feature does a good job exposing the public
fields that you can access when you simply type the name of
your object followed by the a . dot/period:
As you can see from the above image, I am able to see all
of the methods and fields that are exposed by the Planet
class that I can access. Besides the gravity,
radius, and GetName
entries in the Auto-Complete box, the rest are part of
default methods that all classes automatically have.
If you are familiar with how object-oriented programming
(OOP) works in other languages such as Java, much of this
tutorial must have been a review for you. If this is your
first introduction to OOP, this tutorial covered a lot of
ground, and I introduced a lot of terminology that you will
encounter in the real world. To best learn this material,
play around and create some simple applications. A tutorial
can only give you the basic idea of how something works, and
its up to you to extend what you learned and apply it in
your own programs.
Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!
|