C# (.NET 8 LTS) track

Lesson 05 · Part 1: Basics

Methods

Break programs into reusable methods and pass data with overloads, ref modifiers and optional arguments.

Part 1: Basics5 min readFree lesson

If you do the same thing in multiple places, the answer is to group it in a method. A method is defined by name, parameters, and return value, and multiple methods can have the same name with different signatures (overloading).

Learning Objectives

  • Understand method declaration and call flow
  • Parameters and return types can be specified
  • Know the meaning and rules of overloading
  • Classify the use of keywords out / ref / in
  • Use optional parameters and named arguments

Key concepts

1) Method declaration and call

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

int s = Add(3, 4);             //call → 7
  • If there is no return value, it is void
  • If declared as static within top-level statements, it can be called from the same file.

2) Overloading

You can have multiple methods with the same name with different signature (parameter Quantity/Type). Different return types are 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 version

3) out / ref / in

Keyword When entering When it comes out Use
out No initialization required Method must be assigned "second return value"
ref Reset required Two-way Modifying caller values ​​directly
in Reset required read-only Reduce copying costs of 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}"); } // in

When calling out/ref, the keyword must be rewritten: Swap(ref x, ref y);.

4) Optional parameters

It can be omitted by giving the last parameter a default value.

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

Greet("exponent");           //Hello, Jisoo!
Greet("Jisoo", "en");    // Hi, Jisoo!

5) Named arguments

If you specify arguments by name, they can be passed regardless of order. Useful for readability and skipping default values.

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

Order(item: "apple", gift: true);    //qty default value is 1, only gift is specified

See as example

Example 1 — Basic/Program.cs: Called separately as a method

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}");

Execution result

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

Note: In top-level statements files, it is common** to group methods **at the end of the file. (The current example is short so I placed it at the top.)

Example 2 — Overloading/Program.cs: same name, different signature

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;
}

Execution result

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

Memo: => is an expression-bodied member — can be written neatly with one line of body. Local functions cannot be overloaded, so if you want to use the same name, you must wrap it in static class.

Example 3 — OutRefIn/Program.cs : Demonstrating out, ref, in

namespace CodingNow.Lecture.Basics05;

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

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

        //in: passing a 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;   // Compilation 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; }
}

Execution result

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

Memo: out int parsed declares variables at the same time at the call site. C# 7+ features.

Example 4 — OptionalNamed/Program.cs: Optional/named arguments

static void Order(string item, int qty = 1, bool gift = false)
{
    string wrap = gift ? "(gift wrapping)" : "";
    Console.WriteLine($"{item} × {qty}{wrap}");
}

Order("apple");                       //default
Order("ship", 3);                       //specify qty
Order(item: "grapes", gift: true);     //By name — qty defaults to 1
Order("Tangerine", qty: 5, gift: true);

Execution result

Apple × 1
Ship × 3
Grapes × 1 (gift wrapped)
Tangerine × 5 (gift wrapped)

Note: Optional parameters must always appear at the end. Centering it requires the caller to specify all of them as named arguments, which is inconvenient.

Common Mistakes

  1. The method name is the same, but only the return type is defined differently — Compilation error. The signature does not include a return type.
  2. Ending with no value given to the out parameter in the method — compilation error.
  3. Missing ref/out keywords when calling — Swap(ref x, ref y), not Swap(x, y).
  4. Default to mutable object for optional parameters — only compile-time constants are allowed.
  5. Too long parameter list — If it exceeds 4-5, consider grouping it into an object.

Cleanup

  • A method is a unit of input (parameters) → processing → output (return value).
  • Overloading only works if the signatures are different**
  • out is "return extra", ref is "two-way", in is "read-only reference"
  • Keep calling code clean with optional and named arguments

Try it yourself

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

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/Basic/Basic.csprojText
<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.csC#
//Call it separately as a method
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.csprojText
<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.csC#
//optional parameters + named arguments
static void Order(string item, int qty = 1, bool gift = false)
{
    string wrap = gift ? "(gift wrapping)" : "";
    Console.WriteLine($"{item} × {qty}{wrap}");
}

Order("apple");                       //default
Order("ship", 3);                      //specify qty
Order(item: "grapes", gift: true);     //by name
Order("Tangerine", qty: 5, gift: true);
src/OutRefIn/OutRefIn.csprojText
<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.csC#
namespace CodingNow.Lecture.Basics05;

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

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

        //in: passing a 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;   // Compilation 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.csprojText
<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.csC#
//Method overloading - same name, different signature
//Since local functions cannot be overloaded, they are wrapped 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;
}

Assignment

Problem 1 — Prime Printer

  • Reference solution: shown below
  • Key concepts: method declaration, for, return value

Requirements

  • Create your own static bool IsPrime(int n) method
  • 1 or less: false
    • 2: true
  • Others: If divisible by 2 ~ √n, it is false
  • Print all decimal numbers between 1 and 50 on one line with for (space separated)

Expected output

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

Hint

  • Find the square root with Math.Sqrt(n) and cast it with (int)
  • It is OK to compare by multiplication like for (int i = 2; i * i <= n; i++)

Problem 2 — TryDivide method (out pattern)

  • Reference solution: shown below
  • Key concepts: out parameter, return value bool

Requirements

  • Create the static bool TryDivide(int a, int b, out int result) method
  • If b == 0, return result = 0; and then false
  • Or return true after result = a / b;
  • Call with two integers as input and output success/failure message

Expected output (success)

a: 10
b: 3
10 / 3 = 3

Expected output (failure)

a: 10
b: 0
It cannot be divided by 0.

Hint

  • The out parameter must be assigned a value in all paths of the method.
  • The call is in the form of if (TryDivide(a, b, out int r)) { ... }

Check the correct answer

After solving it yourself, compare it with the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

Homework01/Homework01.csprojText
<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>
Homework01/Program.csC#
//Decimal output from 1 to 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("Decimals from 1 to 50:");
for (int i = 1; i <= 50; i++)
{
    if (IsPrime(i))
        Console.Write($"{i} ");
}
Console.WriteLine();
Homework02/Homework02.csprojText
<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>
Homework02/Program.csC#
//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("It cannot be divided by 0.");

Learn this lesson with a mentor

Work through the C# (.NET 8 LTS) track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now