← Back to C# series
🟣
Basics
Basics Β· Prerequisite: previous lecture

05. Methods

A method is a named, reusable bundle of code. Cover parameters, return values, overloading, ref/out/in, params, and expression-bodied methods.

C#.NET 8basicsmethods
Duration
⏱ ~1-1.5 hours
Level
πŸ“Š Beginner
Prerequisite
🎯 Previous lecture or equivalent
OUTCOME
A method is a named, reusable bundle of code. Cover parameters, return values, overloading, ref/out/in, params, and expression-bodied methods.

What you'll learn

  • 1Understand method declaration and invocation
  • 2Specify parameters and return types
  • 3Know the meaning and rules of **overloading**
  • 4Distinguish the purposes of `out` / `ref` / `in`
  • 5Use optional parameters and named arguments

Overview

If you do the same thing in multiple places, wrap it in a **method**. A method is defined by its name, parameters, and return value β€” and you can have multiple methods sharing the same name with different signatures (overloading).

Core Concepts

1) Method declaration and call

csharp
static int Add(int a, int b)   // return type  parameters
{
    return a + b;
}

int s = Add(3, 4);             // call β†’ 7
  • Use `void` when there's no return value
  • Inside top-level statements, declare as `static` to call from the same file

2) Overloading

You can have multiple methods with the **same name** if their **signature (parameter count/types)** differs. Differing only by return type is not overloading.

csharp
static int Add(int a, int b) => a + b;
static double Add(double a, double b) => a + b;

int    i = Add(1, 2);       // int version
double d = Add(1.5, 2.5);   // double version

3) `out` / `ref` / `in`

KeywordOn entryOn exitUse
`out`no init needed**must be assigned** by the method"second return value"
`ref`must be initializedtwo-waydirectly modify the caller's value
`in`must be initializedread-onlyavoid copy cost for large structs
csharp
if (int.TryParse("42", out int n))   // out
    Console.WriteLine(n);

static void Swap(ref int a, ref int b) { (a, b) = (b, a); }   // ref
static void Print(in Point p) { Console.WriteLine($"{p.X},{p.Y}"); } // in

At the call site you must restate the keyword: `Swap(ref x, ref y);`.

4) Optional parameters

Give a default value to trailing parameters to make them optional.

csharp
static void Greet(string name, string lang = "en")
{
    Console.WriteLine(lang == "ko" ? $"Annyeong, {name}!" : $"Hi, {name}!");
}

Greet("Jisoo");           // Hi, Jisoo!
Greet("Jisoo", "ko");     // Annyeong, Jisoo!

5) Named arguments

Specifying arguments **by name** lets you pass them out of order. Useful for readability and skipping defaults.

csharp
static void Order(string item, int qty = 1, bool gift = false) { /* ... */ }

Order(item: "Apple", gift: true);    // qty defaults to 1, only gift specified

Examples

Example 1 β€” `Basic/Program.cs`: extract and call methods

csharp
static int Add(int a, int b)
{
    return a + b;
}

int x = Add(3, 4);
int y = Add(10, 20);

Console.WriteLine($"Add(3, 4)  = {x}");
Console.WriteLine($"Add(10,20) = {y}");

**Output**

text
Add(3, 4)  = 7
Add(10,20) = 30

**Note:** In a top-level-statements file it's typical to **collect methods near the bottom**. (Shown at the top here because the example is short.)

Example 2 β€” `Overloading/Program.cs`: same name, different signatures

csharp
Console.WriteLine($"Calc.Add(1, 2)       = {Calc.Add(1, 2)}");
Console.WriteLine($"Calc.Add(1.5, 2.5)   = {Calc.Add(1.5, 2.5)}");

static class Calc
{
    public static int Add(int a, int b) => a + b;
    public static double Add(double a, double b) => a + b;
}

**Output**

text
Calc.Add(1, 2)       = 3
Calc.Add(1.5, 2.5)   = 4

**Note:** `=>` is an **expression-bodied member** β€” a clean form when the body is a single expression. Local functions can't be overloaded, so for shared names wrap them in a `static class`.

Example 3 β€” `OutRefIn/Program.cs`: `out`, `ref`, `in` demo

csharp
namespace CodingNow.Lecture.Basics05;

internal static class Program
{
    public static void Main()
    {
        // out: TryParse pattern
        if (int.TryParse("123", out int parsed))
            Console.WriteLine($"Parse success: {parsed}");

        // ref: swap two values
        int x = 1, y = 2;
        Swap(ref x, ref y);
        Console.WriteLine($"After Swap: x={x}, y={y}");

        // in: pass by read-only reference
        Point p = new(10, 20);
        Print(in p);
    }

    static void Swap(ref int a, ref int b)
    {
        (a, b) = (b, a);
    }

    static void Print(in Point p)
    {
        // p.X = 0;   // compile error: in is read-only
        Console.WriteLine($"Point({p.X}, {p.Y})");
    }
}

internal readonly struct Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) { X = x; Y = y; }
}

**Output**

text
Parse success: 123
After Swap: x=2, y=1
Point(10, 20)

**Note:** `out int parsed` **declares the variable at the call site**. C# 7+ feature.

Example 4 β€” `OptionalNamed/Program.cs`: optional + named arguments

csharp
static void Order(string item, int qty = 1, bool gift = false)
{
    string wrap = gift ? " (gift-wrapped)" : "";
    Console.WriteLine($"{item} Γ— {qty}{wrap}");
}

Order("Apple");                       // defaults
Order("Pear", 3);                     // qty specified
Order(item: "Grape", gift: true);     // by name β€” qty defaults to 1
Order("Tangerine", qty: 5, gift: true);

**Output**

text
Apple Γ— 1
Pear Γ— 3
Grape Γ— 1 (gift-wrapped)
Tangerine Γ— 5 (gift-wrapped)

**Note:** Optional parameters must always be **at the end**. Placing one in the middle forces every caller to use named arguments β€” awkward.

Full example code (src/)

src/Basic/Basic.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics05</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

src/Basic/Program.cs

csharp
// extract and call methods
static int Add(int a, int b)
{
    return a + b;
}

int x = Add(3, 4);
int y = Add(10, 20);

Console.WriteLine($"Add(3, 4)  = {x}");
Console.WriteLine($"Add(10,20) = {y}");

src/OptionalNamed/OptionalNamed.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics05</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

src/OptionalNamed/Program.cs

csharp
// optional parameters + named arguments
static void Order(string item, int qty = 1, bool gift = false)
{
    string wrap = gift ? " (gift-wrapped)" : "";
    Console.WriteLine($"{item} Γ— {qty}{wrap}");
}

Order("Apple");                       // defaults
Order("Pear", 3);                     // qty specified
Order(item: "Grape", gift: true);     // by name
Order("Tangerine", qty: 5, gift: true);

src/OutRefIn/OutRefIn.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics05</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

src/OutRefIn/Program.cs

csharp
namespace CodingNow.Lecture.Basics05;

internal static class Program
{
    public static void Main()
    {
        // out: TryParse pattern
        if (int.TryParse("123", out int parsed))
            Console.WriteLine($"Parse success: {parsed}");

        // ref: swap two values
        int x = 1, y = 2;
        Swap(ref x, ref y);
        Console.WriteLine($"After Swap: x={x}, y={y}");

        // in: pass by read-only reference
        Point p = new(10, 20);
        Print(in p);
    }

    static void Swap(ref int a, ref int b)
    {
        (a, b) = (b, a);
    }

    static void Print(in Point p)
    {
        // p.X = 0;   // compile error: in is read-only
        Console.WriteLine($"Point({p.X}, {p.Y})");
    }
}

internal readonly struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

src/Overloading/Overloading.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics05</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

src/Overloading/Program.cs

csharp
// Method overloading - same name, different signatures
// Local functions can't be overloaded, so we wrap in a static class.
Console.WriteLine($"Calc.Add(1, 2)       = {Calc.Add(1, 2)}");
Console.WriteLine($"Calc.Add(1.5, 2.5)   = {Calc.Add(1.5, 2.5)}");

static class Calc
{
    public static int Add(int a, int b) => a + b;
    public static double Add(double a, double b) => a + b;
}

Common Mistakes

  1. Defining methods with the same name and **only the return type different** β€” compile error. The signature does not include the return type.
  2. Not assigning the `out` parameter on every code path inside the method β€” compile error.
  3. Forgetting the `ref`/`out` keyword at the call site β€” it's `Swap(ref x, ref y)`, not `Swap(x, y)`.
  4. Trying to use a **mutable object as the default** for an optional parameter β€” only compile-time constants are allowed.
  5. Parameter list too long β€” at 4-5+ parameters, consider grouping into an object.

Summary

  • A method is a unit of input (parameters) β†’ processing β†’ output (return value)
  • Overloading requires **different signatures**
  • `out` is "extra return", `ref` is "two-way", `in` is "read-only reference"
  • Optional + named arguments keep call sites clean

Practice

**Practice - 05. Methods**

Problem 1 β€” Prime printer

  • Project folder: `Homework01/`
  • Key concepts: method declaration, `for`, return value

Requirements

  • Write a `static bool IsPrime(int n)` method yourself
  • ≀ 1: `false`
  • 2: `true`
  • Otherwise: if any divisor in `2 ~ √n` divides evenly, `false`
  • Use a `for` loop to print all primes from 1 to 50 on one line (space-separated)

Expected output

text
Primes 1 to 50:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47

Hints

  • Use `Math.Sqrt(n)` and cast to `int`
  • Or compare with multiplication: `for (int i = 2; i * i <= n; i++)`

Problem 2 β€” `TryDivide` method (`out` pattern)

  • Project folder: `Homework02/`
  • Key concepts: `out` parameter, `bool` return

Requirements

  • Write `static bool TryDivide(int a, int b, out int result)`
  • If `b == 0`: set `result = 0;` and return `false`
  • Otherwise: `result = a / b;` and return `true`
  • Read two ints, call it, and print success/failure

Expected output (success)

text
a: 10
b: 3
10 / 3 = 3

Expected output (failure)

text
a: 10
b: 0
Cannot divide by zero.

Hints

  • The `out` parameter must be assigned on every code path
  • Call style: `if (TryDivide(a, b, out int r)) { ... }`

Check your answer

Try it yourself, then compare against the [`answer/`](./answer/) folder.

Answer (answer/)

homework/answer/Homework01/Homework01.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics05</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

homework/answer/Homework01/Program.cs

csharp
// Print primes 1..50
static bool IsPrime(int n)
{
    if (n < 2) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;

    for (int i = 3; i * i <= n; i += 2)
    {
        if (n % i == 0) return false;
    }
    return true;
}

Console.WriteLine("Primes 1 to 50:");
for (int i = 1; i <= 50; i++)
{
    if (IsPrime(i))
        Console.Write($"{i} ");
}
Console.WriteLine();

homework/answer/Homework02/Homework02.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics05</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

homework/answer/Homework02/Program.cs

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

Console.Write("a: ");
int a = int.Parse(Console.ReadLine()!);

Console.Write("b: ");
int b = int.Parse(Console.ReadLine()!);

if (TryDivide(a, b, out int r))
    Console.WriteLine($"{a} / {b} = {r}");
else
    Console.WriteLine("Cannot divide by zero.");

Try It Yourself

bash
cd src/Basic && dotnet run
cd ../Overloading && dotnet run
cd ../OutRefIn && dotnet run
cd ../OptionalNamed && dotnet run

Next Lecture

[../../02_OOP/06_Classes_and_Objects](../../02_%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5/06_%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80_%EA%B0%9D%EC%B2%B4/) β€” Step up to classes that bundle data with behavior.

Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—