Site

Functions — C# Methods

Tutorial 4.0  •  C# / Learn

4.0 What This Teaches

This tutorial covers how C# defines and calls methods:

4.1 Method Syntax

In top-level statement files, you define methods directly - no surrounding class needed:
static int Add(int a, int b)
{
    return a + b;
}

static void Greet(string name)
{
    Console.WriteLine($"Hello, {name}!");
}

// calling them
int result = Add(3, 4);
Greet("Alice");
The return type comes first. void means the method returns nothing. static is required in top-level statement context.

4.2 Parameter Passing

Value types pass a copy by default - the caller's variable is not affected. Use ref to pass by reference, or out to return a value through a parameter:
static void Double(ref int x)
{
    x *= 2;   // modifies caller's variable
}

static bool TryDivide(int a, int b, out double result)
{
    if (b == 0) { result = 0; return false; }
    result = (double)a / b;
    return true;
}

int n = 5;
Double(ref n);
Console.WriteLine(n);   // 10

if (TryDivide(10, 3, out double quotient))
    Console.WriteLine($"quotient = {quotient:F3}");
out parameters must be assigned before the method returns. The caller does not need to initialize them before passing.

4.3 Default Parameters and Named Arguments

static void Log(string message, string level = "INFO", bool timestamp = false)
{
    string prefix = timestamp ? $"[{DateTime.Now:HH:mm}] " : "";
    Console.WriteLine($"{prefix}[{level}] {message}");
}

Log("Server started");                         // uses defaults
Log("Disk full", "WARN");                      // overrides level
Log("Crash", level: "ERROR", timestamp: true); // named arguments
Named arguments let you specify parameters out of order and make call sites more readable. Parameters with defaults must come after required ones.

4.4 Method Overloading

Multiple methods can share the same name as long as their parameter lists differ. The compiler selects the right one based on argument types:
static void Print(int x)    => Console.WriteLine($"int: {x}");
static void Print(double x) => Console.WriteLine($"double: {x}");
static void Print(string s) => Console.WriteLine($"string: {s}");

Print(42);       // int: 42
Print(3.14);     // double: 3.14
Print("hello");  // string: hello

4.5 Returning Multiple Values

Return a tuple to deliver multiple values. Callers can deconstruct it:
static (int min, int max) MinMax(int a, int b)
{
    return a < b ? (a, b) : (b, a);
}

var (lo, hi) = MinMax(9, 3);
Console.WriteLine($"min={lo}, max={hi}");  // min=3, max=9
Named tuple elements (min, max) make the return type self-documenting. Callers can also access elements by position: .Item1, .Item2.

4.6 Expression-Bodied Methods

When a method body is a single expression, use the => shorthand:
static int Square(int x) => x * x;
static bool IsEven(int n) => n % 2 == 0;
static double CircleArea(double r) => Math.PI * r * r;
Expression-bodied methods are identical in behavior to the full form. Use them when the logic is simple enough to read in one line.

4.7 Local Functions

A local function is defined inside another method. It is only visible within that method - useful for recursive helpers or validation logic:
static long Factorial(int n)
{
    if (n < 0) throw new ArgumentException("n must be non-negative");
    return Compute(n);

    long Compute(int k) => k <= 1 ? 1 : k * Compute(k - 1);
}

Console.WriteLine(Factorial(10));  // 3628800

4.8 Example - All Together

// Functions - method definitions and calling patterns.

Console.WriteLine(Add(3, 4));
Console.WriteLine(Describe(42));
Console.WriteLine(Describe("hello"));

var (lo, hi) = Bounds(-5, 12, 3, -1, 7);
Console.WriteLine($"lo={lo}, hi={hi}");

static int Add(int a, int b) => a + b;
static string Describe(int x) => $"int: {x}";
static string Describe(string s) => $"string: {s}";

static (int lo, int hi) Bounds(params int[] values)
{
    int lo = values[0], hi = values[0];
    foreach (int v in values)
    {
        if (v < lo) lo = v;
        if (v > hi) hi = v;
    }
    return (lo, hi);
}
Expected output:
7
int: 42
string: hello
lo=-5, hi=12

4.9 Exercise

Exercise
  • Write a Clamp(int value, int min, int max) method that returns value clamped to the range [min, max]. Write it as an expression-bodied method.
  • Write overloads of Clamp for double.
  • Write a Swap<T>(ref T a, ref T b) method that swaps two values. Test it with both int and string.

4.10 Common Mistakes

Forgetting return in a non-void method

If a code path in a non-void method does not reach a return statement, the compiler reports "not all code paths return a value". Every path through the method must return a value of the declared type.

Expecting ref to work with value type copies

static void Double(int x) { x *= 2; }  // modifies a copy, not the caller's variable
int n = 5;
Double(n);
Console.WriteLine(n);  // still 5, not 10
Add ref to both the method signature and the call site to modify the caller's variable.

Ambiguous overloads

If two overloads match equally well (e.g., one takes int and another takes long and you pass a literal 0), the compiler reports an ambiguous call error. Make the call explicit with a cast: Method((long)0).

4.11 Key Terms

TermMeaning
methodA named block of code with a return type and parameters
voidReturn type indicating the method returns no value
refPasses a variable by reference; caller and callee share the same location
outLike ref but the parameter does not need to be initialized before calling
overloadingMultiple methods with the same name but different parameter lists
default parameterA parameter with a fallback value used when the caller omits it
named argumentSpecifying a parameter by name rather than position at the call site
tupleA lightweight grouping of values with optional named fields
expression-bodied memberMethod written as => expression instead of a block
local functionA function defined inside another method; not visible outside it