Methods, Return Types, and Constructors
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


In this lesson, we'll explore methods. We've used methods extensively in previous lessons, so you'll be familiar with a lot of the material. We'll also discuss concepts around method parameters, and reintroduce class constructors.

Methods
Class Function Members

Methods are one member of a set of class functions that provide some means of manipulating class data. Below is a complete list of these class functions, with a short description for each. This lesson will cover many of these functions, while others will be covered later. In addition to class function members, we also have class data members that include fields (class variables using a public access modifier), and constants (unchangeable fields). Classes also include class event members for dealing with events.

  • Property functions are specialized functions used to access private class variable data, often referred to as accessors, or gets and sets. The C# property syntax defines each uniquely, thus setting this class of functions apart from the method syntax we'll cover in this lesson, and that you're already familiar with. Properties provide a "method-like" interface for accessing class variables.
  • Methods, the focus of this lesson, are class constructs used to further manipulate class data or provide some other type of interaction with the users of a class relevant to the purpose of the class. Methods can be instance methods, the type we'll consider in this lesson, or static methods, a type we'll consider in a future lesson. An instance method requires an instance of a class before the method can be invoked.
  • Constructors are class functions using a specialized syntax called whenever a class is initialized. We'll also discuss constructors in this lesson.
  • Finalizers are the opposite of constructors, called when an object is destroyed. As objects are created, and then no longer used by going out of scope, or when an application domain is shut down, finalizers are called to free up the resources used by objects before the objects themselves are destroyed. We'll discuss these in a later lesson.
  • Operator functions allow for the overloading of other C# operators, such as the + operator, when used with an object. We'll discuss these in a later lesson.
  • Indexer functions allow objects to be indexed similar to an array or a collection, and will also be covered in a future lesson.
Class Methods

In C#, methods must be defined within a class definition. Below is the syntax definition of a method. Square brackets indicate an optional element.

OBSERVE: Method Syntax Definition
[access modifiers] return_type methodName ( [parameters] )
{
    // Method body
}

From the syntax definition, you can see that a class method declaration consists of an optional access modifier, a return type, the name of the method, an optional list of input parameters in parentheses, and the method body, surrounded by {curly braces}. A value of void may be used for the return type to indicate the method does not return a value. Method definitions that do include a valid return type must use the return keyword to return a value. We've specified camelCase for the method name, although you may use any valid method naming convention you prefer.

We'll discuss each of the method syntax elements below.

Access Modifiers

An access modifier determines the restrictions placed on the calling of a method. As indicated, an access modifier is optional. If omitted, the default access is internal access. The available access modifiers are listed below. You may also reference Microsoft's online MSDN article Visual Studio 2010 Access Modifiers.

  • public: The type or member can be accessed by any other code in the same assembly or another assembly that references it.
  • private: The type or member can be accessed only by code in the same class or struct.
  • protected: The type or member can be accessed only by code in the same class or struct, or in a class that is derived from that class.
  • internal: The type or member can be accessed by any code in the same assembly, but not from another assembly.
  • protected internal: The type or member can be accessed by any code in the assembly in which it is declared, or from within a derived class in another assembly. Access from another assembly must take place within a class declaration that derives from the class in which the protected internal element is declared, and it must take place through an instance of the derived class type.

Typically, best programming practices encourage the use of explicitly declaring the access modifier for a method. You'll note that most of the methods we've created in our lessons have been private. We'll examine the difference between private and internal in our sample program shortly.

NoteIn programming parlance, calling a method is also known as invoking a method.

Method Parameters

Method parameters are optional, but when used consist of one or more parameter definitions, separated by commas. Below is a method parameter syntax definition. Note the use again of square brackets to indicate optional elements.

Method Parameter Definition
( [parameter_modifier] data_type parameterName [default] )

Each parameter definition consists of an optional parameter modifier, the data type of the paramater, a valid parameter name, and an optional default value.

Parameter modifiers consist of the following options. You may also reference Microsoft's online MSDN article Visual Studio 2010 Method Parameters.

  • ref: Parameter values are passed by reference, requiring prior initialization of the argument. We will discuss this modifier later in this lesson.
  • out: Parameter values are passed by reference without prior initialization of the method argument. We will discuss this modifier in a later lesson.
  • params: Specifies a method paramater that takes a variable number of arguments. We will discuss this modifier in a later lesson.

If no parameter modifier is specified, a parameter is said to be passed by value. We'll discuss this concept more later in the lesson.

NoteWhat is the difference between a method parameter and a method argument? These two terms are often interchanged, but they have very distinct meanings. A method parameter is the name of the parameter used within the method definition and referenced within the body of the method. A method argument is the value that is said to be passed to the method when the method is invoked. So, a method parameter refers to the name of the parameter, whereas the method argument is the value of the parameter.

No two methods within the same class may have the same method name and parameter list; otherwise, the program wouldn't know which version of the method to call. This unique method-name-plus-parameter-type-list is also known as a method's fingerprint or signature. Note that the parameter list does not refer to the names of the parameters, but their data types.

Method Return Types

A method return type may either be void or a specific data type. If a specific data type is indicated, the method definition must use the return keyword to return a value of the specified data type.

NoteThe void return type indicates that a method does not return a value; however, a method may still include the return keyword, indicating an exit point from the method. A method may have as many return statements as required by the program, or no return statements, allowing the program to exit at the conclusion of execution of all lines of code within the method. Multiple exit points are not considered a best practice, and should be avoided.
Coding: Camel I

Let's use the information we've learned so far to create a program that involves a fictitious camel, and more specifically, the hump of the camel. We're going to make it possible to add or remove water from the hump of our camel. It should be noted that camels don't actually store water in their hump, but a reservoir of fatty tissue that when metabolized can yield more than one gram of water per one gram of fat. Let's get started!

Create a new Visual Studio Windows Forms Application named Camels. Change the name Form1.cs to Camels.cs, and change the titlebar Text property to Camels. Click to save your changes.

Now let's add a few controls. Note that we're using NumericUpDown and ProgressBar controls.

Modify the Camels Form by adding the controls listed below. Change each control's Name property to match the Name Property column, and each Text property to match the Text Property column. Arrange the controls as shown:

ObjectName PropertyText Property
LabelwaterLabelCamel Water Amount:
LabelwaterAmountLabel0
NumericUpDownwaterAmountNumericUpDown
ButtonaddWaterButton+
ButtonremoveWaterButton--
ProgressBarwaterAmountProgressBar
ButtonexitButtonExit

Click to save your changes.

Next, let's add all of the event handlers we'll need, including one for the Camels Form Load event. You can add the events by double-clicking on the control, by selecting the correct events using the Events property page, or by a mixture of both methods.

Add the following events to the Camel Form. The double-clicking method will work for all of the listed events, as the methods are the default event handler for each control or Form.

ControlEventEvent Name
addWaterButtonClickaddWaterButton_Click
removeWaterButtonClickremoveWaterButton_Click
Camels FormLoadCamels_Load
exitButtonClickexitButton_Click

Next, we'll add a new class for handling our camel's hump, and the code to close the Form.

Select the Camels.cs Form Code Editor, and modify the code as shown below.

Camels.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Camels
{
    public partial class Camels : Form
    {
        private Hump _hump = new Hump();
        
        public Camels()
        {
            InitializeComponent();
        }
        
        private void addWaterButton_Click(object sender, EventArgs e)
        {
        }
        
        private void removeWaterButton_Click(object sender, EventArgs e)
        {
        }
        
        private void Camels_Load(object sender, EventArgs e)
        {
        }
        
        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
    
    internal class Hump
    {
        private int _gallonsOfWater;
        private const int _maximumGallonsOfWater = 100;
        
        internal Hump()
        {
            _gallonsOfWater = 0;
        }
        
        internal int GallonsOfWater
        {
            get { return _gallonsOfWater; }
        }
        
        internal int MaximumGallonsOfWater
        {
            get { return _maximumGallonsOfWater; }
        }
        
        internal void addWater(int gallonsToAdd)
        {
            _gallonsOfWater += gallonsToAdd;
            if (_gallonsOfWater > _maximumGallonsOfWater)
                _gallonsOfWater = _maximumGallonsOfWater;
        }
        
        internal void removeWater(int gallonsToRemove)
        {
            _gallonsOfWater -= gallonsToRemove;
            if (_gallonsOfWater < 0)
                _gallonsOfWater = 0;
        }
    }
}

to save your changes. You can run it, but it doesn't do much yet. Let's discuss the code we've added so far.

OBSERVE: Camels.cs
.
.
.
namespace Camels
{
    public partial class Camels : Form
    {
        private Hump _hump = new Hump();
.
.
.        
        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
    
    internal class Hump
    {
        private int _gallonsOfWater;
        private const int _maximumGallonsOfWater = 100;
        
        internal Hump()
        {
            _gallonsOfWater = 0;
        }
        
        internal int GallonsOfWater
        {
            get { return _gallonsOfWater; }
        }
        
        internal int MaximumGallonsOfWater
        {
            get { return _maximumGallonsOfWater; }
        }
        
        internal void addWater(int gallonsToAdd)
        {
            _gallonsOfWater += gallonsToAdd;
            if (_gallonsOfWater > _maximumGallonsOfWater)
                _gallonsOfWater = _maximumGallonsOfWater;
        }
        
        internal void removeWater(int gallonsToRemove)
        {
            _gallonsOfWater -= gallonsToRemove;
            if (_gallonsOfWater < 0)
                _gallonsOfWater = 0;
        }
    }
}

Are you curious about the addition of a second Hump class in the same file? Yes, that's right, you can have more than one class within the same physical file. In a later lesson, we'll even discuss nesting classes within classes! But for now, we've added this class to illustrate a couple of points. The Hump class could just as easily have been defined in a separate own file. Notice that when you define a second class you must make that class, or any additional classes, have the internal access modifier. We've also changed all of the accessors and methods that would normally be public to also be internal to illustrate the use of this keyword.

The purpose of the Hump class is to provide a means of creating a _hump object we can use to add, remove, and track the amount of water stored in the camel's hump. The _hump object also includes code to make sure we don't attempt to remove more water than we have, or add more water than we can store. We use the read-only property _gallonsOfWater to store the current amount of water. Why is this property called a property, and how is it read-only? _gallonsOfWater is a property because it's a private class variable, and we've defined a get accessor. Because there is only a get accessor, and no put accessor, other classes can only read the value, and not assign a value, making the property read-only. You should also note that we've used the return keyword in our property accessors, just as we would have done if they'd been methods.

We've also defined a constant _maximumGallonsOfWater, using the const keyword. We use this constant to make sure we can't add more water than we can hold.

Finally, we added the standard code to make sure our application exits when the exitButton is clicked.

So far, our application doesn't allow us to add or remove water, so let's add that functionality, along with code to display the amount of water, and update our ProgressBar control to graphically display the water amount.

Select the Camels.cs Form Code Editor, and modify the code as shown below.

Camels.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Camels
{
    public partial class Camels : Form
    {
        private Hump _hump = new Hump();
        
        public Camels()
        {
            InitializeComponent();
        }
        
        private void addWaterButton_Click(object sender, EventArgs e)
        {
            _hump.addWater(Int32.Parse(waterAmountNumericUpDown.Value.ToString()));
            updateWaterDisplay();
        }
        
        private void removeWaterButton_Click(object sender, EventArgs e)
        {
            _hump.removeWater(Int32.Parse(waterAmountNumericUpDown.Value.ToString()));
            updateWaterDisplay();
        }
        
        private void updateWaterDisplay()
        {
            waterAmountLabel.Text = _hump.GallonsOfWater.ToString();
            waterAmountProgressBar.Value = _hump.GallonsOfWater;
        }
        
        private void Camels_Load(object sender, EventArgs e)
        {
            waterAmountProgressBar.Minimum = 0;
            waterAmountProgressBar.Maximum = _hump.MaximumGallonsOfWater;
        }
        
        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
    
    internal class Hump
    {
        private int _gallonsOfWater;
        private const int _maximumGallonsOfWater = 100;
        
        internal Hump()
        {
            _gallonsOfWater = 0;
        }
        
        internal int GallonsOfWater
        {
            get { return _gallonsOfWater; }
        }
        
        internal int MaximumGallonsOfWater
        {
            get { return _maximumGallonsOfWater; }
        }
        
        internal void addWater(int gallonsToAdd)
        {
            _gallonsOfWater += gallonsToAdd;
            if (_gallonsOfWater > _maximumGallonsOfWater)
                _gallonsOfWater = _maximumGallonsOfWater;
        }
        
        internal void removeWater(int gallonsToRemove)
        {
            _gallonsOfWater -= gallonsToRemove;
            if (_gallonsOfWater < 0)
                _gallonsOfWater = 0;
        }
    }
}

and to run the program. Add water, enough to exceed 100 gallons, and then remove enough water to make it less than zero, to make sure it works. Let's discuss the latest changes.

OBSERVE: Camels.cs
.
.
.
namespace Camels
{
    public partial class Camels : Form
    {
        private Hump _hump = new Hump();
        
        public Camels()
        {
            InitializeComponent();
        }
        
        private void addWaterButton_Click(object sender, EventArgs e)
        {
            _hump.addWater(Int32.Parse(waterAmountNumericUpDown.Value.ToString()));
            updateWaterDisplay();
        }
        
        private void removeWaterButton_Click(object sender, EventArgs e)
        {
            _hump.removeWater(Int32.Parse(waterAmountNumericUpDown.Value.ToString()));
            updateWaterDisplay();
        }
        
        private void updateWaterDisplay()
        {
            waterAmountLabel.Text = _hump.GallonsOfWater.ToString();
            waterAmountProgressBar.Value = _hump.GallonsOfWater;
        }
        
        private void Camels_Load(object sender, EventArgs e)
        {
            waterAmountProgressBar.Minimum = 0;
            waterAmountProgressBar.Maximum = _hump.MaximumGallonsOfWater;
        }
.
.
.
    }
    
    internal class Hump
    {
.
.
.        
        internal void addWater(int gallonsToAdd)
        {
            _gallonsOfWater += gallonsToAdd;
            if (_gallonsOfWater > _maximumGallonsOfWater)
                _gallonsOfWater = _maximumGallonsOfWater;
        }
        
        internal void removeWater(int gallonsToRemove)
        {
            _gallonsOfWater -= gallonsToRemove;
            if (_gallonsOfWater < 0)
                _gallonsOfWater = 0;
        }
    }
}

Our naming conventions for variables, methods, and controls should enable you to read the code easily and determine how it works. We call either _hump.addWater() or _hump.removeWater() to add or remove water. We added the updateWaterDisplay() method to refresh the textual and graphical display of the amount of water in the camel's hump. Since our Hump class does boundary checking on the water amount, we don't have to worry about trying to add or remove too much water.

We've also added code to the Camels_Load event handler to initialize the minimum and maximum range of the ProgressBar control. The Camels_Load event is called just before the Form is displayed for the first time.

The ref Keyword and Passing By Value Versus Passing By Reference

Previously, we mentioned that parameters can have parameter modifiers. For this lesson, we want to introduce the ref keyword, and show the difference between passing an argument to a method by value and by reference.

By default, an argument passed to a method as a parameter is said to be passed by value, meaning that the value of the argument is copied into the method, and that any changes made to this copy do not result in a change to the value of the original argument in the code that called the method. This rule applies to all data types, although in a future lesson we'll discuss the difference between value data types and reference data types. For this lesson, we are only concerned with value data types, such as integer.

When you use a parameter modifier like ref, the modifier is used both in the method parameter list definition (the fingerprint), and when you invoke the method, preceding the argument with the ref keyword. The ref parameter modifier requires that any method argument passed by reference be initialized before being passed.

Let's modify our Camels.cs project to use the ref keyword. We'll add a new Form event handler for the Shown event, an event that occurs when a Form is shown for the first time.

Select the Camels.cs (Design) Editor, select the Form (by clicking the title bar), select the Events icon in the Properties window, and double-click the Shown event. Add code to the Camels_Shown event handler, and add the testChangeInteger method as shown.

Camels.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Camels
{
    public partial class Camels : Form
    {
        private Hump _hump = new Hump();
        
        public Camels()
        {
            InitializeComponent();
        }
        
        private void addWaterButton_Click(object sender, EventArgs e)
        {
            _hump.addWater(Int32.Parse(waterAmountNumericUpDown.Value.ToString()));
            updateWaterDisplay();
        }
        
        private void removeWaterButton_Click(object sender, EventArgs e)
        {
            _hump.removeWater(Int32.Parse(waterAmountNumericUpDown.Value.ToString()));
            updateWaterDisplay();
        }
        
        private void updateWaterDisplay()
        {
            waterAmountLabel.Text = _hump.GallonsOfWater.ToString();
            waterAmountProgressBar.Value = _hump.GallonsOfWater;
        }
        
        private void Camels_Load(object sender, EventArgs e)
        {
            waterAmountProgressBar.Minimum = 0;
            waterAmountProgressBar.Maximum = _hump.MaximumGallonsOfWater;
        }
        
        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        
        private void testChangeInteger(ref int value)
        {
            value += 10;
        }
        
        private void Camels_Shown(object sender, EventArgs e)
        {
            int changeInteger = 0;
        
            testChangeInteger(ref changeInteger);
            MessageBox.Show("changeInteger value: " + changeInteger);
        }
    }
    
    internal class Hump
    {
        ... no change ...
    }
}

and to save and run the program. As soon as the Form is displayed, a MessageBox dialog box appears, showing that changeInteger is 10. Let's discuss how it works.

OBSERVE:
.
.
.
        private void testChangeInteger(ref int value)
        {
            value += 10;
        }
        
        private void Camels_Shown(object sender, EventArgs e)
        {
            int changeInteger = 0;
            
            testChangeInteger(ref changeInteger);
            MessageBox.Show("changeInteger value: " + changeInteger);
        }
    }
.
.
.

Without the ref parameter modifier (try it!), the call to the testChangeInteger() method would have no impact on the changeInteger argument and the Message Box would show the value as 0. By adding ref, the addition of 10 to the value parameter is really referencing the changeInteger argument.

Class Constructors

Earlier in the lesson, we listed the various class function members, which in addition to methods includes constructors. We've used constructors before, and the Camels project we've worked on is no exception. A class may have no constructor, in which case C# will provide a default constructor that simply creates an instance of the object, or in OOP language, instantiates it, and initializes any class member variables to their default values. Default values for data types are what you might expect: false for boolean data types, and 0 for numeric data types. Also see Microsoft's online MSDN article Default Values Table.

Of course, a class may also have one or more constructors. A constructor with no parameters is called the default constructor, and is the constructor automatically provided by C# when no constructors are coded. Below is the syntax definition of a constructor.

Constructor Syntax Definition
[access modifiers] constructorName ( [parameters] )
{
    // Constructor body
}

The syntax definition of a constructor is very similar to that of a method, except that a constructor does not have a return type. You should note that typically you use an access modifier that allows the constructor to be invoked when instantiating your classes. If you use a private access modifer for a constructor, that constructor will never be called. Just as with methods, which constructor is called when a class is created is determined by its signature, the parameters included with the call to the constructor.

NoteYou can code a private constructor, but if it's the default constructor, you will prevent C# from automatically adding a default constructor, making your class uninstantiable. If you have other constructors with different parameters, one of these other constructors would have to be called to enable creating an instance of your class. There are situations where you may not want a class to be created, and using a private default constructor would accomplish this task, although a better method is to make the class static, a scenario we'll cover in a later lesson.

We'll continue to discuss constructors as we proceed with the other lesson topics. Constructors have a number of other features we can take advantage of, but many of them are in the context of other C# constructs and language features.

Before you move on to the next lesson, do your homework! Right-click in the window where this lesson text appears and select Back. Then select Quiz for this lesson in the syllabus and answer the quiz questions. When you finish the quiz questions, click HAND IT IN at the bottom of that window. Then do the same with the Project(s) for the lesson. Your instructor will grade your quiz(zes) and project(s) and provide guidance if needed.