This lesson introduces you to C# Methods. Our objectives are as
follows:
- Understand the structure of a method.
- Know the difference between static and instance methods.
- Learn to instantiate objects.
- Learn how to call methods of an instantiated object.
- Understand the 4 types of parameters.
- Learn how to use the "this" reference.
Previously, all of our functionality for each program resided in the Main()
method. While this was adequate for the simple programs we used to learn
earlier concepts, there is a better way to organize your program, using
methods. Methods are extremely useful because they allow you to separate
your logic into different units.
The structure of a method is as follows:
attributes modifiers
return-type method-name ( parameters ) { statements }
We defer discussion of attributes and modifiers to a later lesson. The
return-type can be any C# type. It can be assigned to a variable for use
later in the program. The method name is a unique identifier for what you
wish to call a method. To promote understanding of your code, a method
name should be meaningful and associated with the task the method
performs. Parameters allow you to pass information to and from a
method. They are surrounded by parenthesis. Statements within the
curly braces carry out the functionality of the method.
Listing 5-1. One Simple Method: OneMethod.cs
using System;
class OneMethod {
public static void Main() {
string myChoice;
OneMethod om = new OneMethod();
do {
myChoice =
om.getChoice();
// Make a
decision based on the user's choice
switch(myChoice) {
case "A":
case "a":
Console.WriteLine("You wish to add an address.");
break;
case "D":
case "d":
Console.WriteLine("You wish to delete an address.");
break;
case "M":
case "m":
Console.WriteLine("You wish to modify an address.");
break;
case "V":
case "v":
Console.WriteLine("You wish to view the address list.");
break;
case "Q":
case "q":
Console.WriteLine("Bye.");
break;
default:
Console.WriteLine("{0} is not a valid choice", myChoice);
}
// Pause to
allow the user to see the results
Console.Write("Press any key to continue...");
Console.ReadLine();
Console.WriteLine();
} while (myChoice != "Q"
&& myChoice != "q"); // Keep going until the user wants to
quit
}
string getChoice() {
string myChoice;
// Print A Menu
Console.WriteLine("My Address
Book\n");
Console.WriteLine("A - Add New
Address");
Console.WriteLine("D - Delete
Address");
Console.WriteLine("M - Modify
Address");
Console.WriteLine("V - View
Addresses");
Console.WriteLine("Q -
Quit\n");
Console.WriteLine("Choice (A,D,M,V,or
Q): ");
// Retrieve the user's choice
myChoice = Console.ReadLine();
return myChoice;
}
}
The program in Listing 5-1 is similar to the DoLoop program from Lesson 4,
except for one difference. Instead of printing the menu and accepting
input in the Main() method, this functionality has been moved to a new method
called getChoice(). The return type is a string. This string is used
in the switch statement in main. The method name "getChoice"
describes what happens when it is invoked. Since the parenthesis are
empty, no information will be transferred to or from the getChoice() method.
Within the method block we first declare the variable "myChoice".
Although this is the same name and type as the "myChoice" variable in
Main(), they are both unique variables. They are local variables and they
are visible only in the block they are declared. In other words, the
"myChoice" in getChoice() knows nothing about the existence of the
"myChoice" in Main(), and visa-versa.
The getChoice() method prints a menu to the console and gets the user's
input. The "return" statement sends the data from the "myChoice"
variable back to the caller, Main(), of getChoice(). Notice that the type
returned by the "return" statement must be the same as the return-type
in the function declaration. In this case it is a string.
In the Main() method we must instantiate a new "OneMethod" object
before we can use getChoice(). This is because of the way getChoice() is
declared. Since we did not specify a "static" modifier (as for
Main()), getChoice() becomes an instance method. The difference between
instance methods and static methods is that multiple instances of a class can be
created (or instantiated) and each instance has it's own separate getChoice()
method. However, when a method is static, there are no instances of that
method, and you can invoke only that one definition of the static method.
So, as stated, getChoice() is not static and therefore, we must instantiate a
new object to use it. This is done with the declaration "OneMethod om
= new OneMethod()". On the left hand side of the declaration is the
object reference "om" which is of type OneMethod. The
distinction of "om" being a reference is important. It is not an
object itself, but it is a variable that can refer (or point ) to an object of
type OneMethod. On the right hand side of the declaration is an assignment
of a new OneMethod object to the reference "om". The keyword
"new" is a C# operator that creates a new instance of an object on the
heap. What is happening here is that a new OneMethod instance is being
created on the heap and then being assigned to the om reference. Now that
we have an instance of the OneMethod object referenced by om, we can manipulate
that instance through the om reference.
Methods, fields, and other class members can be accessed, identified, or
manipulated through the "." (dot) operator. Since we want to
call getChoice(), we do so by using the dot operator through the om
reference: "om.getChoice()". The program then executes the
statements in the getChoice() block and returns. To capture the value
getChoice() returns, we use the "=" (assignment) operator. The
returned string is placed into Main()'s local myChoice variable. From
there, the rest of the program executes as you expect, using concepts from
earlier lessons.
Listing 5-2. Method Parameters: MethodParams.cs
- using System;
class Address {
public string name;
public string address;
}
class MethodParams {
public static void Main() {
string myChoice;
MethodParams mp = new
MethodParams();
do {
// show
menu and get input from user
myChoice
= mp.getChoice();
// Make a
decision based on the user's choice
mp.makeDecision(myChoice);
// Pause
to allow the user to see the results
Console.Write("Press any key to continue...");
Console.ReadLine();
Console.WriteLine();
} while (myChoice !=
"Q" && myChoice != "q"); // Keep going until the
user wants to quit
}
// show menu and get user's choice
string getChoice() {
string myChoice;
// Print A Menu
Console.WriteLine("My
Address Book\n");
Console.WriteLine("A - Add
New Address");
Console.WriteLine("D -
Delete Address");
Console.WriteLine("M -
Modify Address");
Console.WriteLine("V - View
Addresses");
Console.WriteLine("Q -
Quit\n");
Console.WriteLine("Choice (A,D,M,V,or
Q): ");
// Retrieve the user's choice
myChoice = Console.ReadLine();
return myChoice;
}
// make decision
void makeDecision(string myChoice) {
Address addr = new Address();
switch(myChoice) {
case
"A":
case
"a":
addr.name = "Joe";
addr.address = "C# Station";
this.addAddress(ref addr);
break;
case
"D":
case
"d":
addr.name = "Robert";
this.deleteAddress(addr.name);
break;
case
"M":
case
"m":
addr.name = "Matt";
this.modifyAddress(out addr);
Console.WriteLine("Name is now {0}.", addr.name);
break;
case
"V":
case
"v":
this.viewAddresses("Cheryl", "Joe", "Matt",
"Robert");
break;
case
"Q":
case
"q":
Console.WriteLine("Bye.");
break;
default:
Console.WriteLine("{0} is not a valid choice", myChoice);
}
}
// insert an address
void addAddress(ref Address addr) {
Console.WriteLine("Name:
{0}, Address: {1} added.", addr.name, addr.address);
}
// remove an address
void deleteAddress(string name) {
Console.WriteLine("You wish
to delete {0}'s address.", name);
}
// change an address
void modifyAddress(out Address addr) {
//Console.WriteLine("Name:
{0}.", addr.name); // causes error!
addr = new Address();
addr.name = "Joe";
addr.address = "C#
Station";
}
// show addresses
void viewAddresses(params string[] names) {
foreach (string name in names) {
Console.WriteLine("Name: {0}", name);
}
}
}
Listing 5-2 is a modification of Listing 5-1, modularizing the program and
adding more implementation to show parameter passing. There are 4 kinds of
parameters a C# method can handle: out, ref, params, and value. To
help illustrate usage of parameters, we created an Address class with two string
fields.
In Main() we call getChoice() to get the user's input and put that string in
the myChoice variable. Then we use myChoice as an argument to makeDecision().
In the declaration of makeDecision() you'll notice it's one parameter is
declared as a string with the name myChoice. Again, this is a new myChoice,
separate from the caller's argument and local only to this method. Since
makeDecision()'s myChoice parameter does not have any other modifiers, it is
considered a "value" parameter. The actual value of the argument
is copied on the stack. Variables given by value parameters are local and
any changes to that local variable do not affect the value of the variable used
in the caller's argument. In other words, value parameters are input only.
The switch statement in makeDecision() calls a method for each case.
These method calls are different that the ones we used in Main(). Instead
of using the "mp" reference, they use the "this"
keyword. "this" is a reference to the current object. We
know the current object has been instantiated because makeDecision() is not a
static method. Therefore, we can use the "this" reference to
call methods within the same instance.
The addAddress() method takes a "ref" parameter. This means
the parameter is passed in as a reference. The reference is copied on the
stack when it is passed to the method. This reference still refers to the
same object on the heap as the original reference used in the caller's
argument. This means any changes to the local reference's object also
changes the caller reference's object. You can think of this as a way to
have an input/output parameter.
modifyAddress() has an "out" parameter. "out"
parameters are only passed back to the calling function. When the method
is called, there is only an unassigned reference placed on the stack.
Because of definite assignment rules, you cannot use this variable until it has
a valid value assigned. The first line in modifyAddress() is commented out
on purpose to illustrate this point. Uncomment it and compile to see what
happens. Once assigned and the program returns, the value of the
"out" parameter will be copied into the caller's argument
variable. You must assign a value to an "out" variable before
your method returns.
A very useful addition to the C# language is the "params"
parameter. This must be a single dimension or jagged array. In
makeDecision() we pass in four comma delimited string arguments. The
number of arguments is variable. In viewAddresses() we use a foreach loop
to print each of these strings. The "params" parameter is
considered an input only parameter and any changes affect the local copy only.
In summary, you understand the structure of a method. You know the four
kinds of parameters a method can declare and their proper usage. When you
wish to use an instance method, you must instantiate it's object as opposed to
static methods that can be called any time. You've also learned how the
"this" reference is used to call instance methods.
C# : Namespaces