Lesson 6 • Intermediate Track
Methods
By the end of this lesson you'll be able to package code into reusable, named methods — passing in parameters, handing back results with return, giving parameters sensible defaults, and overloading one name for several jobs. This is how real programs stay organised instead of becoming one giant block of code.
What You'll Learn
- Define methods with parameters and call them with arguments
- Return a value with return, or use void for actions with no result
- Use optional (default), named, and params arguments
- Hand back multiple values with out parameters (the TryParse pattern)
- Overload one method name for different parameter signatures
- Write expression-bodied methods and understand variable scope
if statements, and loops like for and foreach — methods are how you bundle that logic up so you can reuse it by name instead of copy-pasting it.💡 Real-World Analogy
A method is like a kitchen appliance. A blender takes ingredients (parameters), processes them, and hands back a smoothie (the return value) — and you never have to know how the motor works inside (that's abstraction). The label on the button is the method name; what you drop in is the arguments. Overloading is a multi-function cooker: one name on the front, but it bakes, steams, or pressure-cooks depending on which settings (parameter types) you give it. Once you've built an appliance, you press its button as many times as you like — that's the whole point of a method.
📊 Method Syntax Reference
| Feature | Syntax | Meaning |
|---|---|---|
| Void method | static void Name() | Does something, returns nothing |
| Return value | static int Name() | Must return an int |
| Expression body | static int F() => expr; | One-liner shorthand for return |
| Optional param | void F(int x = 5) | Uses 5 if the caller omits it |
| Params array | void F(params int[] n) | Any number of arguments |
| Out parameter | bool F(out int r) | Hands back an extra value |
| Named args | F(name: "Bo", age: 9) | Pass by name, any order |
Parameter = the placeholder named in the method's definition. Argument = the actual value you pass in when you call it. Same slot, two words — they trip everyone up at first.
1. Defining & Calling Methods
A method has a return type (what it gives back), a name, and a list of parameters (its inputs). Use void when it just does something with no result to report; use a type like int, string or double when it should hand a value back with return. Simple one-liners can use the expression-bodied form => instead of { return ...; }. Read this worked example first, run it, then you'll finish one yourself.
Worked example: void, return & expression-bodied
Read every comment, run it, and check each result matches.
using System;
class Program
{
// A void method DOES something but returns nothing.
// Shape: static void Name(parameters) { ... }
static void Greet(string name)
{
Console.WriteLine($"Hello, {name}! 👋"); // prints, returns nothing
}
// A method with a RETURN TYPE (int) must hand a value back.
// int Add(int a, int b)
// |--| |-| |--------|
// return name parameters (the inputs)
static int Add(int a, int b)
{
return a + b;
...Your turn. The method below is meant to return the area of a rectangle, but the return type and the return expression are blank. Fill in the two ___ blanks using the hints, then run it.
🎯 Your turn: complete a method that returns a value
Fill in the return type and the return expression, then check the area is 20.
using System;
class Program
{
// 🎯 YOUR TURN — fill in the blanks marked with ___ then run it.
// Goal: finish a method that RETURNS the area of a rectangle.
// 1) The method must hand back a number, so its return type is int.
static ___ RectangleArea(int width, int height) // 👉 return type: int
{
// 2) Multiply the two parameters and send the answer back.
return ___; // 👉 the expression: width * height
}
static void Main()
{
...2. Parameters — Optional, Named, Params & Out
Optional parameters carry a default value, so callers can leave them off (they must come after all required parameters). Named arguments let you pass values by name in any order, which makes calls self-documenting. params accepts any number of arguments as an array. And out lets a method hand back more than one value at once — it's exactly how int.TryParse returns both a success flag and the parsed number.
Worked example: optional, named, params & out
See default values, named calls, variable-length params, and out parameters in action.
using System;
class Program
{
// OPTIONAL parameters have a default. They MUST come after required ones.
static void PrintMessage(string msg, int times = 1, string prefix = "→")
{
for (int i = 0; i < times; i++)
Console.WriteLine($"{prefix} {msg}"); // repeats 'times' lines
}
// params — accept ANY number of arguments as an array.
static int Sum(params int[] numbers)
{
int total = 0;
foreach (int n in numbers) total += n;
...Now you try. The method below greets someone, with a greeting that should default to "Hello". Fill in the default value and the two calls — one using the default, one overriding it.
🎯 Your turn: parameters with a default value
Give the greeting a default, call it once with and once without, then check both lines.
using System;
class Program
{
// 🎯 YOUR TURN — fill in the blanks marked with ___ then run it.
// Goal: greet someone, with an OPTIONAL greeting that defaults to "Hello".
// 1) Give 'greeting' a default value of "Hello" so callers can skip it.
static void Welcome(string name, string greeting = ___) // 👉 default: "Hello"
{
Console.WriteLine($"{greeting}, {name}!");
}
static void Main()
{
// 2) Call Welcome with ONLY a name — it should use the
...3. Overloading, Scope & Recursion
Method overloading means giving several methods the same name but different parameter types or counts — the compiler picks the right one from the arguments you pass (note: the return type alone can't tell two overloads apart; the parameters must differ). Scope is where a name is visible: a variable declared inside a method is local to it and disappears when the method ends. Recursion is a method that calls itself — handy for problems that shrink toward a base case that stops the recursion.
Worked example: overloading, scope & recursion
Watch the compiler choose overloads by signature, and see a recursive factorial unwind.
using System;
class Program
{
// OVERLOADING — same name, DIFFERENT parameters. The compiler
// picks the version that matches the arguments you pass.
static int Multiply(int a, int b) => a * b;
static double Multiply(double a, double b) => a * b;
static int Multiply(int a, int b, int c) => a * b * c;
// SCOPE: 'a', 'b', 'c' above are LOCAL — they only exist inside
// their own method. A variable made in Main can't be seen here.
// RECURSION — a method that cal
...🔎 Deep Dive: passing by value vs ref / out
By default a method gets a copy of the value you pass (this is "pass by value"). Changing the parameter inside the method does not change the caller's variable. To let a method change the caller's variable, use ref (in and out) or out (out only — the method must assign it before returning).
static void AddOne(int n) { n++; } // works on a COPY
static void AddOneRef(ref int n) { n++; } // works on the ORIGINAL
int x = 5;
AddOne(x); // x is still 5 (the copy was changed, not x)
AddOneRef(ref x); // x is now 6 (ref reached back to the original)Use out when a method needs to produce an extra result (like TryParse). Use ref when it needs to read and update an existing value. Most of the time, prefer returning a value (or a tuple) over ref — it's clearer.
Putting It Together: a Tiny Shopping Cart
Here's a small program that uses everything from this lesson at once — a params method to total any number of prices, an expression-bodied method with an optional tax rate, a void printer, and a named argument to override the default. You understand every line now.
Worked example: shopping cart
Change the prices or the tax rate and watch the subtotal and total update.
using System;
class Program
{
// === Tiny shopping cart — every method here uses this lesson's skills ===
// Returns a value; uses an OPTIONAL tax rate parameter.
static decimal WithTax(decimal amount, decimal rate = 0.20m)
=> amount + amount * rate; // expression-bodied, returns decimal
// params — sum any number of item prices into a subtotal.
static decimal Subtotal(params decimal[] prices)
{
decimal sum = 0;
foreach (decimal p in pr
...Notice how each method does one job and has a name that says what it does. That's the habit to build: small, well-named methods read like a description of the program.
Pro Tips
- 💡 One method, one job: if a method is over ~20 lines or does several things, split it. Small methods are easier to name, test, and reuse.
- 💡 Name methods as verbs:
CalculateTotal,IsValid,GetUser— the name should say what it does or returns. - 💡 Prefer returning a value (or tuple) over
out:static (int min, int max) Range(int[] a)reads more cleanly than twooutparameters. - 💡 Use expression bodies for one-liners:
static bool IsEven(int n) => n % 2 == 0;is clean and clear. - 💡 Keep parameter lists short: more than three or four parameters usually means the data belongs together in a class or record (next lesson).
Common Errors (and the fix)
- "CS0161: not all code paths return a value": a method with a return type must
returnon every path. If you haveif (x) return a;with no finalreturn, add a fallbackreturnafter theif. - "CS1501: No overload for method 'X' takes N arguments": you called the method with the wrong number of arguments. Check the definition — e.g.
Add(5)whenAdd(int a, int b)needs two. - "CS0029: Cannot implicitly convert type 'string' to 'int'": you returned the wrong type. A
static int GetName()that returns text won't compile — match the return type to what you actually return (here, change it tostring). - "CS0127: Since 'X' returns void, a return keyword must not be followed by an object expression": a
voidmethod tried toreturna value. Either drop the value (return;on its own is fine for an early exit) or change the method's return type. - "CS1737: Optional parameters must appear after all required parameters": you put a defaulted parameter before a required one. Reorder so all
= defaultparameters come last. - Overloading by return type only: two methods with the same name and parameters but different return types won't compile — the parameters must differ.
📋 Quick Reference
| Task | Code | Notes |
|---|---|---|
| Action, no result | static void Log(string m) | No return value |
| Return a value | static int Add(int a, int b) | return a + b; |
| One-liner | static int Sq(int n) => n * n; | Expression body |
| Default value | void F(int x = 0) | Optional param (last) |
| Call by name | F(x: 5) | Named argument |
| Many arguments | int Sum(params int[] n) | Sum(1, 2, 3) |
| Extra output | bool TryX(out int r) | Assign r before return |
| Overload | F(int) / F(double) | Params must differ |
Frequently Asked Questions
Q: What's the difference between a parameter and an argument?
A parameter is the named placeholder in the method's definition — int a in Add(int a, int b). An argument is the real value you pass when you call it — the 5 in Add(5, 3). Definition side = parameter; call side = argument.
Q: When should I use void vs a return type?
Use void when the method's job is to do something (print, save, send) and there's no answer to hand back. Use a return type when the caller needs a result — a total, a grade, a true/false. If you find yourself printing inside a method and the caller also wants the value, return it instead and let the caller print.
Q: What's the difference between out and ref?
Both let a method change the caller's variable. out is "output only" — the method must assign it before returning, and the caller doesn't need to set it first (used by TryParse). ref is "in and out" — the value must be set before the call and the method can read and change it.
Q: Why won't C# let me overload by return type?
When you write Multiply(2, 3), the compiler chooses the overload from the arguments, not from what you do with the result. Two methods that differ only in return type would be ambiguous, so C# requires the parameter lists to differ.
Mini-Challenge: Write a Max Method
No blanks this time — just a brief and an outline. Write a method static int Max(int a, int b) that returns the larger of its two arguments, then call it on a few pairs in Main and print the results. Run it and check your output against the expected lines in the comments.
🎯 Mini-Challenge: int Max(int a, int b)
Write the method yourself, call it on three pairs, and match the expected output.
using System;
class Program
{
// 🎯 MINI-CHALLENGE: write a Max method
// 1. Write a method static int Max(int a, int b)
// that RETURNS the larger of the two numbers.
// (Hint: an if statement, or the ternary a > b ? a : b)
// 2. In Main, call it on a few pairs and print the results.
//
// ✅ Expected output:
// Max(3, 9) = 9
// Max(12, 4) = 12
// Max(7, 7) = 7
// write your Max method here
static void Main()
{
// ca
...🎉 Lesson Complete
- ✅ A method bundles code under a name with parameters (inputs) you call using arguments
- ✅
voidmethods perform actions; typed methods hand a value back withreturn - ✅ Expression-bodied syntax (
=>) is shorthand for a one-linereturn - ✅ Parameters can be optional (default), named, or
paramsfor any count - ✅
outhands back extra values;refreads and updates the caller's variable - ✅ Overloading reuses a name across different parameter signatures; scope keeps locals private to their method
- ✅ Next lesson: Object-Oriented Programming — group data and methods together into classes and objects
Sign up for free to track which lessons you've completed and get learning reminders.