Method Advanced
Method Overloading
Method overloading allows defining multiple methods with the same name in the same class, as long as their parameter lists differ. A different parameter list means a different number, type, or order of parameters. The return type does not participate in overload resolution.
Example
using System;
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
class Program
{
static void Main()
{
Calculator calc = new Calculator();
Console.WriteLine(calc.Add(1, 2));
Console.WriteLine(calc.Add(1.5, 2.5));
Console.WriteLine(calc.Add(1, 2, 3));
}
}
3
4
6
Optional and Named Parameters
Optional parameters assign default values to parameters in the method declaration; these parameters can be omitted when calling the method. Named parameters pass values by specifying parameter names, without having to follow positional order.
Example
using System;
class Program
{
static void Print(string msg, int count = 1, string prefix = "")
{
for (int i = 0; i < count; i++)
{
Console.WriteLine($"{prefix}{msg}");
}
}
static void Main()
{
Print("Hello");
Print("Hi", 3);
Print(msg: "World", prefix: "> ");
Print(prefix: ">> ", count: 2, msg: "Test");
}
}
Hello
Hi
Hi
Hi
> World
>> Test
>> Test
The ref Parameter Modifier
The ref keyword passes a parameter by reference — modifications to the parameter inside the method affect the caller's variable. The variable must be initialized before the call, and the ref keyword must also be used at the call site.
Example
using System;
class Program
{
static void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
static void Main()
{
int x = 10;
int y = 20;
Console.WriteLine($"Before: x={x}, y={y}");
Swap(ref x, ref y);
Console.WriteLine($"After: x={x}, y={y}");
}
}
Before: x=10, y=20
After: x=20, y=10
The out Parameter Modifier
The out keyword also passes by reference, but the method must assign a value to the out parameter, and the variable does not need to be initialized before the call. It is commonly used when a method needs to return multiple values.
Example
using System;
class Program
{
static bool TryParseInt(string s, out int result)
{
result = 0;
foreach (char c in s)
{
if (c < '0' || c > '9')
return false;
result = result * 10 + (c - '0');
}
return true;
}
static void Main()
{
if (TryParseInt("123", out int number))
{
Console.WriteLine($"Parsed: {number}");
}
if (!TryParseInt("abc", out int fail))
{
Console.WriteLine("Parse failed");
}
}
}
Parsed: 123
Parse failed
out parameters must be assigned before the method returns. Starting with C# 7, you can declare variables directly at the call site.
The in Parameter Modifier
The in keyword passes a parameter by reference as read-only — the method cannot modify the value of an in parameter. It is useful for passing large structs to avoid copy overhead while preventing accidental modification.
Example
using System;
struct BigData
{
public long A, B, C, D, E, F, G, H;
}
class Program
{
static void Process(in BigData data)
{
Console.WriteLine($"A={data.A}, B={data.B}");
}
static void Main()
{
BigData d = new BigData { A = 100, B = 200 };
Process(in d);
Process(d);
}
}
A=100, B=200
A=100, B=200
ℹ️ Note: The
inkeyword can be omitted at the call site; the compiler will automatically pass by reference.
params Variable Arguments
The params keyword allows a method to accept a variable number of arguments — the compiler collects them into an array. A method can only have one params parameter, and it must be the last in the parameter list.
Example
using System;
class Program
{
static int Sum(params int[] numbers)
{
int total = 0;
foreach (int n in numbers)
{
total += n;
}
return total;
}
static void Main()
{
Console.WriteLine(Sum(1, 2, 3));
Console.WriteLine(Sum(10, 20, 30, 40, 50));
Console.WriteLine(Sum());
}
}
6
150
0
params can only be used with single-dimensional arrays, and a method can have at most one params parameter.
Recursion Principles and Classic Examples
Recursion is a programming technique where a method calls itself. Every recursion must have a termination condition (base case); otherwise, it will cause a stack overflow.
Factorial
using System;
class Program
{
static long Factorial(int n)
{
if (n <= 1)
return 1;
return n * Factorial(n - 1);
}
static void Main()
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($"{i}! = {Factorial(i)}");
}
}
}
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
Fibonacci Sequence
using System;
class Program
{
static int Fibonacci(int n)
{
if (n <= 0) return 0;
if (n == 1) return 1;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
static void Main()
{
for (int i = 0; i <= 10; i++)
{
Console.Write($"{Fibonacci(i)} ");
}
Console.WriteLine();
}
}
0 1 1 2 3 5 8 13 21 34 55
Local Functions
C# 7 introduced local functions, allowing methods to be defined inside other methods. Local functions can only be accessed within their containing method and are useful for extracting helper logic used only in a specific method.
Example
using System;
class Program
{
static int[] GetEvenSquares(int[] numbers)
{
bool IsEven(int n) => n % 2 == 0;
int Square(int n) => n * n;
System.Collections.Generic.List<int> result = new System.Collections.Generic.List<int>();
foreach (int n in numbers)
{
if (IsEven(n))
{
result.Add(Square(n));
}
}
return result.ToArray();
}
static void Main()
{
int[] data = { 1, 2, 3, 4, 5, 6, 7, 8 };
int[] result = GetEvenSquares(data);
Console.WriteLine(string.Join(", ", result));
}
}
4, 16, 36, 64
Variable Scope
A variable's scope determines where it can be accessed. In C#, there are three main scope levels: block scope (inside if/for/while blocks), method scope (local variables defined within a method), and class scope (fields defined in a class).
Example
using System;
class ScopeDemo
{
int classField = 100;
void MethodA()
{
int localVar = 10;
Console.WriteLine($"classField={classField}, localVar={localVar}");
if (true)
{
int blockVar = 20;
Console.WriteLine($"blockVar={blockVar}");
}
}
void MethodB()
{
Console.WriteLine($"classField={classField}");
}
}
class Program
{
static void Main()
{
ScopeDemo demo = new ScopeDemo();
demo.MethodA();
demo.MethodB();
}
}
classField=100, localVar=10
blockVar=20
classField=100
✅ Rule: You cannot declare local variables with the same name in the same method. Inner code blocks can access outer variables, but outer blocks cannot access inner block variables.
❓ FAQ
📖 Summary
- Method overloading: same name, different parameters; return type does not participate
- Optional parameters: assign default values at declaration, must be placed after required parameters
- Named parameters: pass values by parameter name, breaking positional constraints
- ref: pass by reference, must initialize before calling
- out: pass by reference, must assign inside the method, no initialization required before calling
- in: read-only pass by reference, prevents modification and avoids copying
- params: variable number of arguments, compiled as an array, only one allowed and must be last
- Recursion: a method calls itself; must have a termination condition; watch for performance and stack overflow
- Local functions: methods defined inside methods, a C# 7+ feature with lower overhead than lambdas
- Variable scope: block < method < class; inner can access outer, outer cannot access inner
📝 Exercises
- Write three overloaded
Multiplymethods accepting two ints, two doubles, and three ints respectively, returning the product. - Write a method
void Log(string msg, string level = "INFO", bool timestamp = true)and call it at least three times using named parameters in different orders. - Write a
void Increment(ref int value)method that increments the passed integer by 1, and verify in Main that the variable is indeed modified. - Write a
bool TryDivide(int a, int b, out double result)method that returns false when b is 0, otherwise returns the division result via out. - Write a
double Average(params double[] values)method that calculates the average of all arguments, handling the empty array case. - Implement a recursive method to calculate 1+2+3+...+n and compare the result with an iterative version.
- Write a method that uses a local function internally to determine whether a number is prime, and returns all prime numbers from 1 to 50.



