What Is A Method?
A method is a block of code this is accessible by name. All statements executed within a C# program run within the context of a method. Every C# program needs, at the very least, one method named Main(). (The name of the method is “Main” but I append the parentheses to the name to make it clear I’m talking about a method.) The following code represents a complete C# program:
using System; public class MethodDemo { public static void Main(){ Console.WriteLine("The Main() method is the program's \"Entry Point\""); } }
The name of the class is MethodDemo. This class is also an application because it contains a Main() method. When the MethodDemo class is loaded into the .NET Runtime VM, control is passed to the Main() method and it calls the Console.WriteLine() method to write the indicated string to the console. Figure 1 shows the results of running the program.
It’s good practice to keep Main() methods short and sweet. This rule is often broken in C# programming textbooks for the sake of demonstration.
Main() Methods Can Be Confusing
The confusing thing about the Main() method is that it’s static, meaning only one Main() method exists for the MethodDemo type. When I first learned Java, (yes, I meant to type Java!) I really struggled to make sense of having the main() (lower case main() in Java) located in a class definition. I was used to C and C++, where methods could exist outside of classes, but it finally clicked after I pressed the “I Believe” button about a thousand times. The trick for me was to think in terms of programs as being a collections of objects that interact with each other. I then developed the habit of creating a separate application class I usually call “MainApp” and put the Main() method there. Let me make a slight alteration to the code:
using System; public class MethodDemo { public static void WritePithyStatements() { Console.WriteLine("Here's a pithy statement!"); } public static void Main(){ Console.WriteLine("The Main() method is the program's \"Entry Point\""); WritePithyStatements(); } }
I’ve added another static method named WritePithyStatements(). When the program loads, the Main() method executes and makes a call to Console.WriteLine() as before. It then calls the WritePithyStatements() method, which in turn makes a call to Console.WriteLine(). Figure 2 shows the results of running this program:
Constructor Methods
Let’s add another type of method to the mix. Take a look at the following code:
using System; public class MethodDemo { public MethodDemo(){ MethodDemo.WritePithyStatements(); Console.WriteLine("Instance of type " + this.GetType() + " created."); } public static void WritePithyStatements(){ Console.WriteLine("Here's a pithy statement!"); } public static void Main(){ Console.WriteLine("The Main() method is the program's \"Entry Point\""); MethodDemo md1 = new MethodDemo(); } }
In this example I have added a special method called a constructor. A constructor method takes the same name as the class within which it is defined. Notice that it has no return type, not even void. Note that to call the static WritePithyStatements() method from the constructor, you must do so via the classname like so:
MethodDemo.WritePithyStatements();
A common mistake made by novice programmers, myself included back in the day, is to mistakenly do something like this:
public void MethodDemo(){ MethodDemo.WritePithyStatements(); Console.WriteLine("Instance of type " + this.GetType() + " created."); }
Note — this may look like a constructor, but it’s not. Fortunately, the C# compiler will let you know you’re making a mistake with the following error:
MethodDemo.cs(6,14): error CS0542: 'MethodDemo': member names cannot be the same as their enclosing type MethodDemo.cs(3,14): (Location of symbol related to previous error)
Instance Methods
Methods can be static, which you’ve seen above, or non-static. Non-static methods are referred to as instance methods. Let me change the static WritePithyStatements() to be non-static and have you examine the results. Here’s the code.
using System; public class MethodDemo { public MethodDemo(){ WritePithyStatements(); Console.WriteLine("Instance of type " + this.GetType() + " created."); } public void WritePithyStatements(){ Console.WriteLine("Here's a pithy statement!"); } public static void Main(){ Console.WriteLine("The Main() method is the program's \"Entry Point\""); MethodDemo md1 = new MethodDemo(); md1.WritePithyStatements(); } }
Can you spot the changes? Notice that you can now call WritePithyStatements() directly from the MethodDemo() constructor. You no longer need to access it via the class name. To call WritePithyStatements() in the Main() method requires accessing it via an instance of MethodDemo. That is, since WritePithyStatements() is now a non-static, instance method, you must first create an object of type MethodDemo and call the method via the object reference, which in this example is named md1. Figure 3 shows the results of running this version of the program:
Returning Values From Methods
Methods, other than the constructor, that simply execute a series of logically related statements and then exit are usually defined with the return type void. That is, they return nothing. If the method does some sort of processing and produces a result, you can specify a return type as is shown in the following code:
using System; public class MethodDemo { public MethodDemo(){ WritePithyStatements(); Console.WriteLine("Instance of type " + this.GetType() + " created."); } public void WritePithyStatements(){ Console.WriteLine("Here's a pithy statement!"); } public int CalculateSum(){ return ((100*(100+1))/2 ); } public static void Main(){ Console.WriteLine("The Main() method is the program's \"Entry Point\""); MethodDemo md1 = new MethodDemo(); md1.WritePithyStatements(); Console.WriteLine(md1.CalculateSum()); } }
In this example I have added a method named CalculateSum() which returns an integer value. This is indicated by the int return type. The CalculateSum() method calculates the sum of the integers 1 – 100. The return keyword is used to return the results of the calculation. Note that because CalculateSum() is an instance method, it must be called via an object reference. Also note that anywhere you can use an integer value, you can call a method that returns an integer value. In the example above, the call to md1.CalculateSum() is evaluated first and will return 5050. This value is then passed to the Console.WriteLine() method, which has been overloaded to handle various argument types. (I’ll cover method arguments in another post.) Figure 4 shows the results of running this program:
Passing Arguments To Methods
The CalculateSum() method is pretty boring in its current state. To make the method more useful you can define one or more method parameters that represent data that can be “passed” to the method for processing. In the example below I have modified the CalculateSum() method to take an integer argument named “n”.
using System; public class MethodDemo { public MethodDemo(){ WritePithyStatements(); Console.WriteLine("Instance of type " + this.GetType() + " created."); } public void WritePithyStatements(){ Console.WriteLine("Here's a pithy statement!"); } public int CalculateSum(int n){ if(n<=0) return 0; return ((n*(n+1))/2 ); } public static void Main(){ Console.WriteLine("The Main() method is the program's \"Entry Point\""); MethodDemo md1 = new MethodDemo(); md1.WritePithyStatements(); Console.WriteLine(md1.CalculateSum(100)); Console.WriteLine(md1.CalculateSum(1)); for(int i=1; i<100; i++){ Console.Write(md1.CalculateSum(i) + ", "); } } }
Figure 5 shows the results of running this program.
Parting Words
Methods are a great way to introduce reuse and modularity into your code. If you find yourself repeatedly typing the same code, it may be a candidate for consilidation into a method. Keep methods focused and no longer than necessary. Select names for methods that reflect their purpose.
Rick Miller
Falls Church, Virginia