Skip to content

C# Methods and Functions

This chapter will detail methods (functions) in C#, including method definition, calling, parameter passing, overloading, and other core concepts, helping you write modular and reusable code.

Method Basics

Method Definition

csharp
// Basic syntax
access_modifier return_type MethodName(parameter_list)
{
    // Method body
    return return_value;  // If return type is not void
}

// Example: Method with no parameters and no return value
public static void SayHello()
{
    Console.WriteLine("Hello, World!");
}

// Example: Method with parameters and return value
public static int Add(int a, int b)
{
    return a + b;
}

// Example: Method with parameters and no return value
public static void PrintMessage(string message)
{
    Console.WriteLine($"Message: {message}");
}

Method Calling

csharp
class Program
{
    static void Main(string[] args)
    {
        // Call method with no parameters
        SayHello();
        
        // Call method with parameters and return value
        int result = Add(5, 3);
        Console.WriteLine($"5 + 3 = {result}");
        
        // Call method with parameters and no return value
        PrintMessage("This is a test message");
    }
    
    static void SayHello()
    {
        Console.WriteLine("Hello, World!");
    }
    
    static int Add(int a, int b)
    {
        return a + b;
    }
    
    static void PrintMessage(string message)
    {
        Console.WriteLine($"Message: {message}");
    }
}

Parameters

Value Parameters

csharp
// Value parameters (default)
public static void Increment(int number)
{
    number++;  // Only affects local copy
    Console.WriteLine($"Inside method: {number}");
}

// Usage
int value = 10;
Increment(value);
Console.WriteLine($"Outside method: {value}");  // Still 10

Reference Parameters (ref)

csharp
// Reference parameters
public static void Increment(ref int number)
{
    number++;  // Affects original variable
    Console.WriteLine($"Inside method: {number}");
}

// Usage
int value = 10;
Increment(ref value);  // Must use ref keyword
Console.WriteLine($"Outside method: {value}");  // Now 11

Out Parameters

csharp
// Out parameters
public static bool TryParseInt(string text, out int result)
{
    return int.TryParse(text, out result);
}

// Usage
string numberText = "42";
if (TryParseInt(numberText, out int number))
{
    Console.WriteLine($"Successfully parsed: {number}");
}
else
{
    Console.WriteLine("Failed to parse");
}

// Discard parameter (C# 7.0+)
if (int.TryParse("123", out _))
{
    Console.WriteLine("Valid number");
}

In Parameters (C# 7.2+)

csharp
// In parameters (read-only)
public static double CalculateArea(in double radius)
{
    // radius = 10;  // Error: Cannot modify in parameter
    return Math.PI * radius * radius;
}

// Usage
double r = 5.0;
double area = CalculateArea(in r);

Params Arrays

csharp
// Params parameter
public static int Sum(params int[] numbers)
{
    int total = 0;
    foreach (int num in numbers)
    {
        total += num;
    }
    return total;
}

// Usage
int sum1 = Sum(1, 2, 3, 4, 5);
int sum2 = Sum(new int[] { 10, 20, 30 });
int sum3 = Sum();  // Empty array, returns 0

Method Overloading

Basic Overloading

csharp
public class Calculator
{
    // Overload 1: Two integers
    public static int Add(int a, int b)
    {
        return a + b;
    }
    
    // Overload 2: Three integers
    public static int Add(int a, int b, int c)
    {
        return a + b + c;
    }
    
    // Overload 3: Two doubles
    public static double Add(double a, double b)
    {
        return a + b;
    }
    
    // Overload 4: Array of integers
    public static int Add(params int[] numbers)
    {
        int sum = 0;
        foreach (int num in numbers)
            sum += num;
        return sum;
    }
}

// Usage
Console.WriteLine(Calculator.Add(5, 3));        // Calls overload 1
Console.WriteLine(Calculator.Add(1, 2, 3));     // Calls overload 2
Console.WriteLine(Calculator.Add(1.5, 2.5));      // Calls overload 3
Console.WriteLine(Calculator.Add(1, 2, 3, 4));  // Calls overload 4

Overloading with Optional Parameters

csharp
public class Greeter
{
    // Method with optional parameters
    public static void Greet(string name, string greeting = "Hello")
    {
        Console.WriteLine($"{greeting}, {name}!");
    }
    
    // Overload with different signature
    public static void Greet()
    {
        Greet("Guest", "Welcome");
    }
}

// Usage
Greeter.Greet("Alice");                    // Uses default greeting
Greeter.Greet("Bob", "Hi");               // Custom greeting
Greeter.Greet();                           // Calls overload

Return Values

Single Return Value

csharp
public static int Divide(int a, int b)
{
    if (b == 0)
        return 0;  // Return default value for error case
    
    return a / b;
}

Multiple Return Values

Using Tuple (C# 7.0+)

csharp
public static (int quotient, int remainder) DivideWithRemainder(int a, int b)
{
    int quotient = a / b;
    int remainder = a % b;
    return (quotient, remainder);
}

// Usage
var (q, r) = DivideWithRemainder(17, 5);
Console.WriteLine($"Quotient: {q}, Remainder: {r}");

Using out Parameters

csharp
public static bool DivideWithRemainder(int a, int b, out int quotient, out int remainder)
{
    if (b == 0)
        return false;
    
    quotient = a / b;
    remainder = a % b;
    return true;
}

// Usage
if (DivideWithRemainder(17, 5, out int q, out int r))
{
    Console.WriteLine($"Quotient: {q}, Remainder: {r}");
}

Local Functions (C# 7.0+)

Basic Local Functions

csharp
public static void ProcessData()
{
    // Local function
    bool IsValid(int number)
    {
        return number > 0 && number < 100;
    }
    
    for (int i = 0; i < 10; i++)
    {
        if (IsValid(i))
            Console.WriteLine($"Valid: {i}");
    }
}

Local Functions with Closures

csharp
public static List<int> FilterNumbers(List<int> numbers, Func<int, bool> predicate)
{
    var result = new List<int>();
    
    foreach (int num in numbers)
    {
        // Local function with closure
        bool ShouldInclude(int n)
        {
            return predicate(n) && n % 2 == 0;
        }
        
        if (ShouldInclude(num))
            result.Add(num);
    }
    
    return result;
}

// Usage
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbers = FilterNumbers(numbers, n => n > 5);

Expression-Bodied Members

Expression-Bodied Methods

csharp
public class MathUtils
{
    // Traditional method
    public static int AddTraditional(int a, int b)
    {
        return a + b;
    }
    
    // Expression-bodied method
    public static int AddExpression(int a, int b) => a + b;
    
    // Expression-bodied method with logic
    public static bool IsEven(int number) => number % 2 == 0;
    
    // Expression-bodied method with multiple statements
    public static string GetGreeting(string name) => 
        string.IsNullOrEmpty(name) ? "Hello!" : $"Hello, {name}!";
}

Recursive Methods

Basic Recursion

csharp
public static int Factorial(int n)
{
    // Base case
    if (n <= 1)
        return 1;
    
    // Recursive case
    return n * Factorial(n - 1);
}

// Usage
Console.WriteLine($"5! = {Factorial(5)}");  // 120

Tail Recursion

csharp
public static int FactorialTail(int n, int accumulator = 1)
{
    if (n <= 1)
        return accumulator;
    
    return FactorialTail(n - 1, n * accumulator);
}

// Usage
Console.WriteLine($"5! = {FactorialTail(5)}");  // 120

Method Modifiers

Static Methods

csharp
public class MathHelper
{
    public static double PI = 3.14159;
    
    public static double CalculateCircleArea(double radius)
    {
        return PI * radius * radius;
    }
    
    public void InstanceMethod()
    {
        Console.WriteLine("This is an instance method");
    }
}

// Usage
double area = MathHelper.CalculateCircleArea(5.0);

// MathHelper helper = new MathHelper();
// helper.InstanceMethod();  // Requires instance

Extension Methods

csharp
// Extension method must be in static class
public static class StringExtensions
{
    public static bool IsNullOrEmpty(this string str)
    {
        return string.IsNullOrEmpty(str);
    }
    
    public static string Reverse(this string str)
    {
        if (string.IsNullOrEmpty(str))
            return str;
        
        char[] chars = str.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }
}

// Usage
string text = "Hello";
bool isEmpty = text.IsNullOrEmpty();  // Extension method
string reversed = text.Reverse();     // Extension method

Generic Methods

Basic Generic Methods

csharp
public static T Max<T>(T a, T b) where T : IComparable<T>
{
    return a.CompareTo(b) > 0 ? a : b;
}

// Usage
int maxInt = Max(5, 10);           // T is int
double maxDouble = Max(3.14, 2.71);  // T is double
string maxString = Max("Apple", "Banana"); // T is string

Generic Methods with Constraints

csharp
public static void PrintCollection<T>(IEnumerable<T> collection) where T : class
{
    Console.WriteLine($"Collection type: {typeof(T).Name}");
    
    foreach (T item in collection)
    {
        Console.WriteLine(item?.ToString() ?? "null");
    }
}

// Usage
var numbers = new List<int> { 1, 2, 3 };
PrintCollection(numbers);

var strings = new List<string> { "A", "B", "C" };
PrintCollection(strings);

Practical Examples

String Utilities

csharp
public static class StringUtils
{
    // Count words in a string
    public static int CountWords(this string text)
    {
        if (string.IsNullOrWhiteSpace(text))
            return 0;
        
        return text.Split(new[] { ' ', '\t', '\n' }, 
                      StringSplitOptions.RemoveEmptyEntries).Length;
    }
    
    // Capitalize first letter
    public static string Capitalize(this string text)
    {
        if (string.IsNullOrEmpty(text))
            return text;
        
        return char.ToUpper(text[0]) + text.Substring(1);
    }
    
    // Truncate with ellipsis
    public static string Truncate(this string text, int maxLength)
    {
        if (string.IsNullOrEmpty(text) || text.Length <= maxLength)
            return text;
        
        return text.Substring(0, maxLength - 3) + "...";
    }
}

Array Utilities

csharp
public static class ArrayUtils
{
    // Find maximum in array
    public static T FindMax<T>(T[] array) where T : IComparable<T>
    {
        if (array == null || array.Length == 0)
            throw new ArgumentException("Array cannot be null or empty");
        
        T max = array[0];
        foreach (T item in array)
        {
            if (item.CompareTo(max) > 0)
                max = item;
        }
        return max;
    }
    
    // Shuffle array
    public static void Shuffle<T>(T[] array)
    {
        Random random = new Random();
        for (int i = 0; i < array.Length; i++)
        {
            int j = random.Next(i, array.Length);
            (array[i], array[j]) = (array[j], array[i]);  // Tuple deconstruction
        }
    }
    
    // Check if array contains element
    public static bool Contains<T>(T[] array, T item)
    {
        foreach (T element in array)
        {
            if (EqualityComparer<T>.Default.Equals(element, item))
                return true;
        }
        return false;
    }
}

Validation Utilities

csharp
public static class ValidationUtils
{
    // Validate email format
    public static bool IsValidEmail(string email)
    {
        if (string.IsNullOrWhiteSpace(email))
            return false;
        
        try
        {
            var addr = new System.Net.Mail.MailAddress(email);
            return addr.Address == email;
        }
        catch
        {
            return false;
        }
    }
    
    // Validate phone number
    public static bool IsValidPhoneNumber(string phone)
    {
        if (string.IsNullOrWhiteSpace(phone))
            return false;
        
        // Remove common formatting
        string cleaned = System.Text.RegularExpressions.Regex.Replace(phone, @"[^\d]", "");
        
        // Check if it's 10 digits (US format)
        return cleaned.Length == 10;
    }
    
    // Validate age range
    public static bool IsValidAge(int age, int min = 0, int max = 150)
    {
        return age >= min && age <= max;
    }
}

Best Practices

Method Design Principles

csharp
// Good: Single responsibility
public static bool IsValidEmail(string email)
{
    // Only validates email format
}

// Good: Descriptive names
public static double CalculateCircleArea(double radius)
{
    // Clear purpose and parameters
}

// Good: Proper error handling
public static int SafeParseInt(string text, int defaultValue = 0)
{
    return int.TryParse(text, out int result) ? result : defaultValue;
}

Parameter Validation

csharp
public static decimal CalculateDiscount(decimal price, decimal discountPercentage)
{
    // Validate parameters
    if (price < 0)
        throw new ArgumentException("Price cannot be negative");
    
    if (discountPercentage < 0 || discountPercentage > 100)
        throw new ArgumentException("Discount percentage must be between 0 and 100");
    
    return price * (1 - discountPercentage / 100);
}

Documentation

csharp
/// <summary>
/// Calculates the compound interest over a period of time.
/// </summary>
/// <param name="principal">The initial amount of money</param>
/// <param name="rate">The annual interest rate (as decimal, e.g., 0.05 for 5%)</param>
/// <param name="years">The number of years</param>
/// <param name="compoundsPerYear">Number of times interest is compounded per year</param>
/// <returns>The final amount after compound interest</returns>
/// <example>
/// <code>
/// double final = CalculateCompoundInterest(1000, 0.05, 10, 12);
/// </code>
/// </example>
public static double CalculateCompoundInterest(
    decimal principal, 
    decimal rate, 
    int years, 
    int compoundsPerYear = 1)
{
    return principal * (decimal)Math.Pow(
        1.0 + (double)(rate / compoundsPerYear), 
        years * compoundsPerYear);
}

Summary

In this chapter, you learned:

  • Method definition and calling
  • Parameter types: value, ref, out, in, params
  • Method overloading
  • Return values and multiple returns
  • Local functions and expression-bodied members
  • Recursive methods
  • Method modifiers: static, extension
  • Generic methods
  • Practical examples and best practices

Methods are fundamental building blocks of C# programs, enabling code reuse, organization, and maintainability. In the next chapter, we'll explore arrays and collections for handling multiple data items efficiently.

Content is for learning and research only.