Breaking Up Code with Methods

Writing all the code for your entire application inside your Main method is a bad idea for many reasons.


Readability – It’s difficult for another developer to understand what your application is doing if you have hundreds of code statements all thrown together in the Main. Understanding the program flow would require a lot of scrolling up and down to follow the logic.

Extensibility – Adding functionality to the application would be problematic since placing new code in the middle of other code could interrupt vital program flow, causing the application to crash or provide invalid data.

Reuse – If all of your code is in the Main method, you will find yourself duplicating code whenever you want to perform the same logic. If you make a change to that logic, you will have to find all the places it was duplicated to make the changes there as well.

Testability – If you were to make a small change to one piece of functionality, you would have to test all the code in Main to make sure you didn’t unintentionally affect another section of code.


To ensure your application is extensible, readable, reusable, and testable, you will want to segment your code into different methods then call those methods when you need to execute the code within them.

Method Elements

Before creating methods, also referred to as “defining methods,” you need to understand the parts of a method. There are five main elements to a method.

Method Body

The method body is the container that holds the code that is to be executed whenever the method is run. The code block of a method body is encased within curly brackets {}. This functionality is similar to the code block of an IF statement or a WHILE loop.

Access Modifier

The access modifier is a keyword that determines where the method can be seen. Items such as methods, fields, properties, and classes have the potential of being seen and utilized throughout your application if they have the correct access modifier. We go into greater detail regarding access modifiers in the post on Classes. There are four main access modifiers used regularly when writing applications.

Public – this is the least restrictive access modifier. Any member with a public modifier can be seen and utilized by any entity within the entire application. It is good practice to avoid granting public access to all of your methods. Ideally, you want to use the most restrictive modifier that you can without affecting the functionality of the application.

Private – this is the most restrictive access modifier. When an item is private, it can only be seen and utilized by entities within the same class. Private is the default modifier for all members of a class, including methods.

Internal – this is the default access modifier for classes. It implies that it can only be seen and utilized by entities within the same project. This concept is discussed in greater detail within the post regarding Project Dependencies

Protected – Protected is used when working with inheritance. This concept is too advanced for this post. It is discussed in the posts regarding Object-Oriented Programming.

A fifth access modifier, “Protected Internal,” is occasionally used when working with inheritance and multiple assemblies. This concept is too advanced for this post. It is discussed in the posts regarding Object-Oriented Programming.

Return Type

As discussed earlier, the body of the method executes multiple lines of code. Sometimes that code will simply perform a process without any information being returned. Those methods will have a “void” return type, meaning the method will not be returning any information when it’s done executing its code. An example of this would be a method who’s purpose is to say a generic “Hello” to the user. There is no information to be gathered or calculated. For this method, the return type would be void.

 private void GreetUser()
 {
 Console.WriteLine("Hello");
 }

There will be other methods that execute its code block to provide some information to the process that has requested the method code be executed. Methods like this will need to provide the data type of the information it will be returning. For example, a method that gets the user's name would have a return type of string since the user’s name will be a text value. We will get into greater detail regarding methods with return types later in this post.

 private string GetUserName()
 {
 Console.WriteLine("What is your name?");
 string input = Console.ReadLine();
 return input;
 }

Method Name

The name of the method is a crucial element, for that is how the application will execute the code inside the method. Somewhere in the application, a process will “call” the name of the method. As long as the method is accessible, the code inside its block will run.

Technically you can use almost any text to name a string. However, there are a few best practices you should follow.

  • The name of the method should be PascalCase. Meaning the first letter in every word is capitalized

  • The name of the method should represent the function of its code block. Some schools of thought feel since the method is “doing” something, its name should be a verb, as the methods in our earlier examples (GreetUser, GetUserName)

  • The name should be as short as possible but specific enough to explain the purpose of the method. The names DoStuff and GetInformation do not give enough detail as to their purpose. The name SayHelloToTheUserInTheConsoleWindow does not need to be so wordy.

Parameter List

Just as a method can return information to the caller after the code block is executed, it can also ask the user for information in the form of parameters. A method can have many parameters of different types that request all sorts of information. We will get into greater detail regarding methods with parameters later in this post.

Optional Modifiers

There are a few optional modifiers that you can use when creating a method that provides additional attributes and properties to the method. Some of these modifiers like “abstract” and “sealed” are discussed in the blog post for Object-Oriented Programming.

We go into detail about the “static” modifier in the post about Classes and Object. However, you have already seen “static” used with your Main method, and you will be required to use it for every method you create in the Program class. For these reasons, we will give a high-level overview of “static” here.

Static

When you are designing and application, there will be some methods that you need to ensure are the only instance of that method in the entire application. There should be no way to create a copy of that method elsewhere in the application and change the functionality.

When a C# application starts up, it looks for a method with the name “Main” to use as the starting point for code execution. Because of this, only one Main method must exist in the project. By using the “static” keyword, we are ensuring that no matter how many copies of the Program class are created, there is only a single Main method.

One rule regarding static methods is that any method it calls within the same class must be static as well. This restriction is why you will see the “static” keyword used in the code examples of this post.

Creating Methods

Methods must exist inside of a Class. When you create a new Console application, the Program class is created for you, and the Main method is where you place your starting code. You can create as many methods as you need within the Program class as long as they exist within the opening and closing brackets of the class.

We have already discussed the elements you need to include when you create a method. Now we are going to dive into the details of the different method types.

Void Methods – No Parameters

Like the GreetUser method from the earlier example, some methods don’t require any additional data and don’t return any value when they execute. These are the easiest methods to create since their elements are pretty generic

Access Modifier -> Void Keyword -> Method Name() -> Method Body

 private static void GreetUser()
 {
 Console.WriteLine("Hello");
 }
 
 private static void DisplayTodaysDate()
 {
 Console.WriteLine($"Today's Date is: {DateTime.Today.ToLongDateString()}.");
 }
 
 private static void ExitApplication()
 {
 Console.WriteLine("Press any key to exit.");
 Console.ReadKey();
 Environment.Exit(0);
 }

Return Methods – No Parameters

The next time of methods to discuss are the methods that don’t require additional information but return a value to the entity requesting the method’s code be executed. These methods take a little more pre-planning because a method can only have one return type. Because of this fact, it is essential to make sure the method returns the information that is needed by the caller.

The RETURN keyword is required to tell your application what value is being returned by the method. Every logic branch in your method must ensure a value is being returned. For example, if you have a RETURN statement in your IF block, you need to have one in your ELSE block as well. Otherwise, you will get a “not all code paths return a value” error.

The RETURN keyword acts as a jump statement. When the application executes a RETURN statement, it will bypass any other code inside the method and return control of the application flow to the entity that requested the method be executed.

private static DateTime GetUsersNextBirtday()
 {
 Console.WriteLine("Please give me the month and day you were born (MM-DD)");
 string input = Console.ReadLine();
 
 string[] numbers = input.Split('-');
 int month = int.Parse(numbers[0]);
 int day = int.Parse(numbers[1]);
 
 // Set their next birthday for this year
 DateTime customerNextBirtday = new DateTime(DateTime.Now.Year, month, day);
 DateTime today = DateTime.Today;
 
 // Did thier birday pass this year
 if (today.Month > month || (today.Month == month && today.Day > day))
 {
 customerNextBirtday = customerNextBirtday.AddYears(1);
 }
 
 return customerNextBirtday;
 }
  
 private static bool ValidateUserPassword()
 {
 string superSecretPassword = "password123";
 Console.WriteLine("Please enter your password.");
 string userPassword = Console.ReadLine().ToLower().Trim();
 
 if (userPassword == superSecretPassword)
 {
 return true;
 }
 else
 {
  return false;
 }
 }

Methods With Parameters

As mentioned earlier, there will be some occasions where the method you are creating needs some additional information from the entity requesting the method executes its code. These pieces of information are called “parameters,” and you declare your parameters between the parenthesis after the method name.

To create a parameter for your method, you declare the variable that will store that information. This declaration is identical to how you declare a variable elsewhere in your code. You provide the data type and the variable name. This variable will be initialized with a value when the caller requests the execution of the method’s code.

For example, if we wanted to up our GreetUser method from earlier and provide a personalized greeting, the GreetUser method will need to know the user’s name. To store this name, we declare a parameter for the method.

 private static void GreetUser(string userName)

As you can see, all I did was declare a variable that will store a string value when someone wants to run the method. Within the code block of the method body, I can use that variable in any of my statements.

 private static void GreetUser(string userName)
 {
 if (userName == "Scott")
 {
 Console.WriteLine("I know your real name is John!");
 }
 else
 {
 Console.WriteLine($"Hello {userName}. How are you.");
 } 
 }

A method can have as many parameters as you feel it needs to execute its code correctly.

private static void DisplayGiftMessage(string kidName, string gift, bool wasGood, string relation )
 {
 Console.WriteLine($"Hello {kidName}, according to your {relation} you want a {gift}");
 if (wasGood)
 {
 Console.WriteLine($"Your {relation} also said that you were good!");
 Console.WriteLine($"It looks like you will be getting a {gift}.");
 Console.WriteLine("Enjoy it!");
 }
 else
  {
 Console.WriteLine($"Your {relation} also said that you were bad!");
 Console.WriteLine($"It looks like you will NOT be getting a {gift}.");
 Console.WriteLine("Better luck next time!");
 }
  }

You can also have a parameter list for methods that return information as well. Sometimes the method will need additional information to return the value the caller is looking to get back. Many calculation and validation methods are of this type.


private double CalculateAnnualBonus(double annualSalary, double bonusPercentage, bool isManagement)
 {
 int managementPerctage = 10;
 
 double calulatedBonus = annualSalary / bonusPercentage;
 double additionalBonus = isManagement ? annualSalary / managementPerctage : 0;
 
 return calulatedBonus + additionalBonus;
 }
  
 private string ValidateUserInput(string input, bool shouldBeInteger, bool shouldBePostiveInteger)
 {
 if (string.IsNullOrEmpty(input))
 {
 return "You must enter some text.";
 }
 if (shouldBeInteger)
 {
 int inputNumber;
 if (int.TryParse(input,out inputNumber) == false)
 {
 return "You must enter a valid integer";
 }
 
 if (shouldBePostiveInteger && inputNumber < 1)
 {
  return "You must enter an integer greater than zero.";
 }
 }
 
 return "The input is valid.";
 
 }

Optional Parameters

In all of the examples so far, the parameters have been required. If the entity calling the method does not provide a value for the parameter, the application will not compile. Ninety percent of the time, this is proper coding. However, there will be times when the caller may not have all of the information you method needs. In cases like this, you will want to make those parameters “optional.”

When you make a parameter optional, you are provided a default value for that variable, allowing your method to executed even if the caller is missing that information. For example, if we go back to our DisplayGiftMethod from earlier, we require that the caller of the method provide a string value for the “relation” parameter. Instead of making that a required parameter, we can make it optional by providing the default of “mother” for the variable.

private static void DisplayGiftMessage(string kidName, string gift, bool wasGood, string relation = "mother" )

With this optional parameter in place, the value of “mother” will be used for the value of relation unless the entity calling the method opts to provide a different value. Take note that Visual Studio expects optional parameters to be placed after required ones when you build your parameter list.

Out Parameters

Earlier in this post, I stated that a method could only have one return type that allows it to provide information to the caller. There will be rare occasions when you need your method to provide additional information to the entity calling your method. Since you can’t add a second return type, there is a particular type of parameter that can be used instead. This parameter is given an OUT modifier to signify that whatever value is inside that variable when the method has finished executing its code will be sent “out” of the method for the caller to use.

A valid use-case for the OUT parameter would be the ValidateUserInput method you saw earlier in this post.

private string ValidateUserInput(string input, bool shouldBeInteger, bool shouldBePostiveInteger)
 {
 if (string.IsNullOrEmpty(input))
 {
 return "You must enter some text.";
 }
 if (shouldBeInteger)
 {
 int inputNumber;
 if (int.TryParse(input,out inputNumber) == false)
 {
 return "You must enter a valid integer";
 }
 
 if (shouldBePostiveInteger && inputNumber < 1)
 {
  return "You must enter an integer greater than zero.";
 }
 }
 
 return "The input is valid.";
 
 }

Ideally, the method should return a Boolean value to provide the caller with a TRUE/FALSE value. This way, an IF statement can be used based on the return of the method. However, we don’t want to lose the different messages that are created based on the result, so we should have a way for the message string to be sent out of the method.

If we implement these changes, the code will look like this:


private bool ValidateUserInput(
 string input, bool shouldBeInteger, bool shouldBePostiveInteger, out string message)
 {
 if (string.IsNullOrEmpty(input))
 {
 message = "You must enter some text.";
 return false;
 }
 if (shouldBeInteger)
 {
 int inputNumber;
 if (int.TryParse(input,out inputNumber) == false)
  {
 message = "You must enter a valid integer";
 return false;
 }
 if (shouldBePostiveInteger && inputNumber < 1)
 {
 message = "You must enter an integer greater than zero.";
 return false;
 }
 }
 
 message = "The input is valid.";
 return true;
 }

All though it’s not required, parameters with the OUT modifier should be placed at the end of your parameter list. This practice improves the readability of the method.

Calling Methods

Now that you have an understanding of what methods are and how to create them, it’s time to learn how to request that they execute their code. When you want a method to do something for you, you ask it the same way you would ask a friend to help you out in a crowded room. You would call out their name. “Calling a method” is the phrase used when your application wants to execute the code within the method. You “call” a method, like you would a friend, by their name.

If we want our console application to utilize the DisplayTodaysDate method, we type its name.

 static void Main(string[] args)
 {
 DisplayTodaysDate();
 }

After typing the method’s name, you need to add the parathesis. The set of parenthesis is what tell Visual Studio that you are executing a method. Without them, Visual Studio will be looking for a variable named DisplayTodaysDate and will give an error saying the variable can’t be found.


We would use the same syntax to call the ExitApplication method.

 static void Main(string[] args)
 {
 
 DisplayTodaysDate();
 
 ExitApplication();
 }

When we run this application, it will execute the code within DisplayTodayDate. Then it will execute the code with ExitApplication.


Calling Methods with a Return Type

Calling a method with a return type is done in the same manner. The only difference is that the information being returned from the method needs to be placed inside a variable. This way, that information can be used later in the application.

 static void Main(string[] args)
 {
 DisplayTodaysDate();
 
 string userName = GetUserName();
 DateTime nextBirthday = GetUsersNextBirtday();
 
 ExitApplication();
 }

This code demonstrates how to set the value of a variable to the information being returned from a method. I have declared a string variable named userName. Then I called the GetUserName method and asked it to provide me with a string value. That value will be stored in the userName variable.

The same thing is being done for the nextBirthday variable, which has a DateTime data type. That variable will contain the DateTime value returned from the GetUsersNextBirthday method.


Arguments vs. Parameters

Before we get into how to call methods that have required parameters, I want to make a quick detour and talk about a few concepts. The first of these concepts is the relationship between arguments and parameters. You have already learned what parameters are. They are variables declared by the method awaiting values from the caller. These values could be literal values, or they could be values stored inside a different variable. The information being passed into the method is called an “argument.” For example, look at the following code.

 static void Main(string[] args)
 {
 GreetUser("Scott");
 
 string userName = GetUserName();
 GreetUser(userName);
 
 ExitApplication();
 }

The first time I call the GreetUser method, the argument “Scott” is passed into the method’s parameter. The second time I call the method, the value that is stored in the userName variable is the argument. It is crucial to understand that when using a variable to pass an argument into a method, the variable name of the argument does not have to the same as the variable name of the parameter.

If we look at the method signature of GreetUser, we see that the parameter name for the string variable is userName.

 private static void GreetUser(string userName)

We don’t have to use a variable named userName when calling the method. The following code would work just as well as the previous example.

 static void Main(string[] args)
 {
 string dudesName = GetUserName();
 GreetUser(dudesName);
 
 ExitApplication();
 }

However, arguments must be valid data types for the parameter. Since GreetUser is expecting a string value, anything else would prevent the code from running. This code would produce an error stating “cannot convert from an int to a string.”

 GreetUser(475);

Value Types vs. Reference Types

It is crucial to understand the difference between value types and reference types when passing arguments into a method. If you are unclear about the differences, you should pause here and read the blog post on the topic.

Passing Value Types as Parameters

When your argument is a value type object, a copy of the value gets passed into the method. The method can do whatever it needs to, but the value of the argument never changes. Take a look at the following method.

static void Main(string[] args)
 {
 int characterHitPoints = 50;
 string characterType = "mage";
 string enemyType = "dragon";
 
 bool didSurvive = DidCharacterSurvive(characterHitPoints, characterType, enemyType);
 Console.WriteLine(characterHitPoints);
 }
 
 private static bool DidCharacterSurvive(int characterHitPoints, string characterType, string enemyType)
 {
 if (enemyType == "dragon")
 {
 characterHitPoints -= 20;
 if (characterType == "mage")
 {
 characterHitPoints -= 20;
 }
 }
 bool didSurvive = characterHitPoints > 0 ? true : false;
 return didSurvive;
 }

Even though characterHitPoints was reduced to 10 in the DidCharacterSurvive method, the console will display 50 when the application runs. That is because the value of 50 was copied from the Main method into the parameter of DidCharacterSurvive.

This concept is pretty straightforward and usually doesn’t cause problems for beginning developers. Things get a little trickier when dealing with reference types.

Passing Reference Types as Parameters

A reference type variable does not store an actual value; it stores a pointer to a location in memory where the value can be found. If your argument is a reference type, you pass a copy of that pointer into the method parameter. The method you are calling will use that reference to update the value that is stored in that memory location.

Why this is important is because it means when the method changes the value of that parameter, the same change will be reflected in the argument. The following code examples will demonstrate this.

We will start with a generic method that will add a value to a list if that value doesn’t already exist.

private static void AddValueToList(List<string> stringList, string value)
 {
 if (stringList.Contains(value) == false)
 {
 stringList.Add(value);
 Console.WriteLine($"The value \"{value}\" has been added to your list.");
 
 }
 else
 {
 Console.WriteLine($"The value \"{value}\" already exists in your list.");
 }
 }

Now we will call the AddValueToList method from our Main method. We will pass the “friends” list and a string literal as arguments into the method.

static void Main(string[] args)
 {
 List<string> friends = new List<string>();
 friends.Add("Jeff");
 
 AddValueToList(friends, "John");
 AddValueToList(friends, "Scott");
 
 foreach (string friend in friends)
 {
 Console.WriteLine(friend);
 }
 
 ExitApplication();
 }

When we run the application, the names “Jeff,” “John,” and “Scott” will be displayed in the console. The “friends” list declared in the Main method was updated my the AddValueToList method without using a return type or OUT parameter.

Because a LIST is a reference type, when we used the “friends” list as an argument, the reference was copied into the “stringList” parameter. Both methods were pointing to the same location in memory, so both methods updated the same List.

Methods Calling Methods

One final concept I want to cover in this post is the ability for methods to call other methods. In our examples so far, we have called the methods one by one inside our main method.

static void Main(string[] args)
 {
 string userName = GetUserName();
 GreetUser(userName);
 
 DateTime usersBirthday = GetUsersNextBirtday();
 Console.WriteLine($"What do you want for your next birtday on {usersBirthday}?");
 
 List<string> friends = new List<string>();
 AddValueToList(friends, "Scott");
 
 ExitApplication();
 }

Most of this code is related to greeting the user. We should move this functionality into the GreetUser method.

 private static void GreetUser()
 {
 string userName = GetUserName();
 Console.WriteLine($"Hello {userName}. How are you.");
 
 DateTime usersBirthday = GetUsersNextBirtday();
 Console.WriteLine($"What do you want for your next birtday on {usersBirthday}?"); 
 }

Once we do that, the main method will look much cleaner.

 static void Main(string[] args)
 {
 GreetUser();
 
 List<string> friends = new List<string>();
 AddValueToList(friends, "Scott");
 
 ExitApplication();
 }

When you run the application, this is the flow of the logic:

  • Main Method – Calls GreetUser();

  • GreetUser – Calls GetUserName();

  • GetUserName – Gets the input from the user and returns the string input

  • GreetUser – Stores that value inside the userName variable

  • GreetUser – Displays a message to the user

  • GreetUser – Calls GetUsersNextBirthday()

  • GetUsersNextBirthday – Gets input from the user and returns the calculated DateTime value

  • GreetUser – Stores that value inside the nextBirthday variable

  • GreetUser – Displays a message to the user

  • Main Method – Creates a new list object

  • Main Method – Calls AddValueToList() passing two arguments

  • AddValueToList – uses the values of the arguments to populate the list

  • Main Method – Calls ExitApplication()

  • ExitApplication – Closes the application

As you can see, methods can call other methods, which in turn can call other methods. If you refer back to the post regarding handling exceptions, you may recall learning about the Call Stack. The Call Stack is responsible for remembering which method calls which so the program will return to the proper method at the proper time.

55 views0 comments

Recent Posts

See All