05. Methods
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
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.
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 version3) `out` / `ref` / `in`
| Keyword | On entry | On exit | Use |
|---|---|---|---|
| `out` | no init needed | **must be assigned** by the method | "second return value" |
| `ref` | must be initialized | two-way | directly modify the caller's value |
| `in` | must be initialized | read-only | avoid copy cost for large structs |
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}"); } // inAt 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.
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.
static void Order(string item, int qty = 1, bool gift = false) { /* ... */ }
Order(item: "Apple", gift: true); // qty defaults to 1, only gift specifiedExamples
Example 1 β `Basic/Program.cs`: 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}");**Output**
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
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**
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
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**
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
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**
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
<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
// 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
<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
// 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
<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
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
<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
// 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
- Defining methods with the same name and **only the return type different** β compile error. The signature does not include the return type.
- Not assigning the `out` parameter on every code path inside the method β compile error.
- Forgetting the `ref`/`out` keyword at the call site β it's `Swap(ref x, ref y)`, not `Swap(x, y)`.
- Trying to use a **mutable object as the default** for an optional parameter β only compile-time constants are allowed.
- 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
Primes 1 to 50:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47Hints
- 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)
a: 10
b: 3
10 / 3 = 3Expected output (failure)
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
<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
// 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
<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
// 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
cd src/Basic && dotnet run
cd ../Overloading && dotnet run
cd ../OutRefIn && dotnet run
cd ../OptionalNamed && dotnet runNext 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.
All lecture materials and example code are openly available on GitHub.
View on GitHub β