Methods and Functions
🎯 What You'll Learn
- What methods are and why they're essential for code organization
- How to define and call methods
- Working with parameters: value, ref, out, and params
- Return values and the void keyword
- Method overloading (same name, different parameters)
- Optional parameters and named arguments
- Expression-bodied methods for concise syntax
- Local functions for encapsulating helper logic
- Recursion: methods that call themselves
- Best practices for writing clean, maintainable methods
What is a Method?
As your programs grow, you'll notice patterns—the same code appearing in multiple places, long sequences of related statements, complex logic that's hard to follow. Methods solve these problems by letting you group code into named, reusable blocks.
A method is a named block of code that performs a specific task. You define it once and can call (execute) it from anywhere in your program, as many times as needed. Methods can accept input (parameters) and return output (return value).
Why Use Methods?
- Reusability: Write once, use many times
- Organization: Break complex problems into smaller, manageable pieces
- Readability:
CalculateTax()is clearer than 20 lines of tax logic - Maintainability: Fix a bug in one place, fixed everywhere
- Testing: Small methods are easier to test than large code blocks
Defining and Calling Methods
Method Syntax
// A simple method with no parameters and no return value
static void SayHello()
{
Console.WriteLine("Hello, World!");
}
// Calling the method
SayHello(); // Output: Hello, World!
SayHello(); // Can call it multiple times
| Part | Example | Description |
|---|---|---|
| Access Modifier | static |
Who can access this method |
| Return Type | void |
What the method gives back (void means nothing) |
| Method Name | SayHello |
How you refer to the method (PascalCase) |
| Parameters | () |
Input values the method accepts |
| Method Body | { ... } |
The code that runs when called |
Methods with Parameters
// One parameter
static void Greet(string name)
{
Console.WriteLine($"Hello, {name}!");
}
Greet("Akwasi"); // Output: Hello, Akwasi!
// Multiple parameters
static void DisplayProduct(string name, decimal price, int quantity)
{
Console.WriteLine($"Product: {name}, Price: {price:C}, Qty: {quantity}");
}
DisplayProduct("Widget", 19.99m, 100);
Methods with Return Values
// Method that returns an int
static int Add(int a, int b)
{
return a + b;
}
int sum = Add(5, 3); // sum = 8
// Method that returns a decimal
static decimal CalculateTotal(decimal price, int quantity)
{
return price * quantity;
}
// Method that returns a bool
static bool IsAdult(int age)
{
return age >= 18;
}
if (IsAdult(25))
{
Console.WriteLine("Welcome!");
}
Parameter Passing: Value, ref, out
Pass by Value (Default)
By default, parameters are passed by value—the method receives a copy.
static void Double(int number)
{
number = number * 2; // Modifies the copy only
}
int x = 10;
Double(x);
Console.WriteLine(x); // Still 10! (unchanged)
Pass by Reference (ref)
static void Double(ref int number)
{
number = number * 2; // Modifies the original!
}
int x = 10;
Double(ref x); // Must use 'ref' when calling
Console.WriteLine(x); // 20 (changed!)
Output Parameters (out)
static void Divide(int dividend, int divisor, out int quotient, out int remainder)
{
quotient = dividend / divisor;
remainder = dividend % divisor;
}
Divide(17, 5, out int q, out int r);
Console.WriteLine($"17 ÷ 5 = {q} remainder {r}"); // 3 remainder 2
// You've already seen this with TryParse!
if (int.TryParse("42", out int result))
{
Console.WriteLine(result);
}
params: Variable Number of Arguments
static int Sum(params int[] numbers)
{
int total = 0;
foreach (int num in numbers)
{
total += num;
}
return total;
}
Console.WriteLine(Sum(1, 2)); // 3
Console.WriteLine(Sum(1, 2, 3, 4, 5)); // 15
Console.WriteLine(Sum()); // 0
Optional Parameters and Named Arguments
// Optional parameters have default values
static decimal CalculatePrice(
decimal basePrice,
int quantity = 1,
decimal taxRate = 0.125m,
decimal discountPercent = 0m)
{
decimal subtotal = basePrice * quantity;
decimal discount = subtotal * (discountPercent / 100m);
decimal afterDiscount = subtotal - discount;
return afterDiscount * (1 + taxRate);
}
// Various ways to call
CalculatePrice(100m); // Just base price
CalculatePrice(100m, 5); // With quantity
CalculatePrice(100m, quantity: 5, discountPercent: 10m); // Named args
Expression-Bodied Methods
// Traditional
static int Add(int a, int b)
{
return a + b;
}
// Expression-bodied (same behavior, more concise)
static int Add(int a, int b) => a + b;
static bool IsPositive(int n) => n > 0;
static decimal CalculateTax(decimal amount) => amount * 0.125m;
static void Log(string msg) => Console.WriteLine($"[LOG] {msg}");
Method Overloading
// Same name, different parameters
static int Add(int a, int b) => a + b;
static int Add(int a, int b, int c) => a + b + c;
static double Add(double a, double b) => a + b;
// Compiler picks the right one
Add(1, 2); // int version
Add(1, 2, 3); // 3-param version
Add(1.5, 2.5); // double version
Local Functions
static decimal CalculateOrderTotal(decimal[] prices, decimal discountPercent)
{
// Local function - only visible inside this method
decimal ApplyDiscount(decimal amount) => amount * (1 - discountPercent / 100m);
decimal subtotal = 0m;
foreach (decimal price in prices)
{
subtotal += price;
}
return ApplyDiscount(subtotal);
}
Recursion
// Factorial: n! = n × (n-1) × ... × 1
static int Factorial(int n)
{
if (n <= 1) return 1; // Base case
return n * Factorial(n - 1); // Recursive case
}
Console.WriteLine(Factorial(5)); // 120
Every recursive method needs a base case to stop. Without it,
you'll get a StackOverflowException.
Best Practices
// 1. Single Responsibility - each method does ONE thing
static bool ValidateOrder(Order order) { ... }
static decimal CalculateTotal(Order order) { ... }
static void SaveOrder(Order order) { ... }
// 2. Descriptive Names - use verbs, be specific
static bool IsUserActive(User user) { ... } // Good
static bool Check(User u) { ... } // Bad
// 3. Keep methods short (20-30 lines max)
// 4. Limit parameters (3-4 max, group into objects if more)
Key Takeaways
- Methods are named, reusable blocks of code that perform specific tasks
- Parameters pass data in; return values send data back
- Default is pass-by-value; use ref or out for pass-by-reference
- params allows variable number of arguments
- Optional parameters have default values; named arguments improve clarity
- Expression-bodied methods (
=>) are concise for simple methods - Method overloading: same name, different parameters
- Local functions encapsulate helper logic within a method
- Recursion: methods calling themselves (always need a base case!)
- Best practices: single responsibility, descriptive names, short methods, few parameters
You now understand how to organize code into reusable methods. In the next section, we'll take this further with classes and objects—the foundation of object-oriented programming.