C# Method Overloading
Create multiple methods with the same name
🔀 What is Method Overloading?
Method overloading allows you to create multiple methods with the same name but different parameters. This provides flexibility by letting one method name handle different types or numbers of inputs automatically.
// Same method name, different parameters
static int Add(int a, int b) { return a + b; }
static double Add(double a, double b) { return a + b; }
Add(5, 3); // Calls int version
Add(5.5, 3.2); // Calls double version
Overloading Concepts
Same Name
Methods share the same name
void Print(int x) { }
void Print(string x) { }
Different Parameters
Vary number or type of parameters
int Add(int a, int b) { }
int Add(int a, int b, int c) { }
Automatic Selection
C# picks the right method
Add(5, 3); // int version
Add(5.5, 3.2); // double version
Code Clarity
One name for related operations
// Better than:
// AddInts, AddDoubles, AddThreeInts
🔹 Overloading by Parameter Count
Method overloading by parameter count provides flexibility with a single method name handling different inputs. You define multiple versions of a method with varying numbers of parameters; C# selects the appropriate one based on the arguments supplied. For example, a Calculate method could accept one, two, or three values. This reduces naming complexity and makes APIs more intuitive, as users don't need to remember distinct names for similar functionality.
class Program
{
// Method with 2 parameters
static int Add(int a, int b)
{
Console.WriteLine("Adding 2 numbers");
return a + b;
}
// Method with 3 parameters
static int Add(int a, int b, int c)
{
Console.WriteLine("Adding 3 numbers");
return a + b + c;
}
// Method with 4 parameters
static int Add(int a, int b, int c, int d)
{
Console.WriteLine("Adding 4 numbers");
return a + b + c + d;
}
static void Main(string[] args)
{
Console.WriteLine("Result: " + Add(5, 3));
Console.WriteLine("Result: " + Add(5, 3, 2));
Console.WriteLine("Result: " + Add(5, 3, 2, 1));
}
}
// Output:
// Adding 2 numbers
// Result: 8
// Adding 3 numbers
// Result: 10
// Adding 4 numbers
// Result: 11
Output:
Adding 2 numbers Result: 8 Adding 3 numbers Result: 10 Adding 4 numbers Result: 11
🔹 Overloading by Parameter Type
Overloading by parameter type allows a method to operate on different data types using the same identifier. For instance, a Print method could accept int, string, or double parameters, executing type-specific logic internally. This enhances code clarity and reuse, as developers can call Print(value) regardless of the value's type, and the compiler resolves to the correct implementation based on the argument's type at compile time.
class Program
{
// Method for integers
static void Display(int number)
{
Console.WriteLine("Integer: " + number);
}
// Method for doubles
static void Display(double number)
{
Console.WriteLine("Double: " + number);
}
// Method for strings
static void Display(string text)
{
Console.WriteLine("String: " + text);
}
// Method for booleans
static void Display(bool value)
{
Console.WriteLine("Boolean: " + value);
}
static void Main(string[] args)
{
Display(42);
Display(3.14);
Display("Hello");
Display(true);
}
}
// Output:
// Integer: 42
// Double: 3.14
// String: Hello
// Boolean: True
Output:
Integer: 42 Double: 3.14 String: Hello Boolean: True
🔹 Combining Count and Type
You can combine parameter count and type overloading for maximum versatility in method design. This approach allows a method like Process to handle (int), (int, int), (string), or (string, int) signatures. The compiler matches the call to the best-fitting overload using both the number and types of arguments. It enables rich, self-descriptive APIs that adapt to diverse usage scenarios while maintaining a clean, consistent interface.
class Program
{
// Two integers
static int Multiply(int a, int b)
{
return a * b;
}
// Two doubles
static double Multiply(double a, double b)
{
return a * b;
}
// Three integers
static int Multiply(int a, int b, int c)
{
return a * b * c;
}
// Integer and double
static double Multiply(int a, double b)
{
return a * b;
}
static void Main(string[] args)
{
Console.WriteLine("2 ints: " + Multiply(5, 3));
Console.WriteLine("2 doubles: " + Multiply(5.5, 2.0));
Console.WriteLine("3 ints: " + Multiply(2, 3, 4));
Console.WriteLine("int & double: " + Multiply(5, 2.5));
}
}
// Output:
// 2 ints: 15
// 2 doubles: 11
// 3 ints: 24
// int & double: 12.5
Output:
2 ints: 15 2 doubles: 11 3 ints: 24 int & double: 12.5
🔹 Practical Example: Area Calculator
A practical example of overloading is an area calculator that computes areas for different shapes using one method name. You can define CalculateArea(double radius) for circles, CalculateArea(double length, double width) for rectangles, and CalculateArea(double base, double height, string shape) for triangles. This makes the code intuitive—users simply call CalculateArea with appropriate arguments, and the correct formula is applied automatically based on the overload match.
class Program
{
// Calculate area of square (one parameter)
static double CalculateArea(double side)
{
Console.WriteLine("Calculating square area");
return side * side;
}
// Calculate area of rectangle (two parameters)
static double CalculateArea(double length, double width)
{
Console.WriteLine("Calculating rectangle area");
return length * width;
}
// Calculate area of triangle (two parameters, different meaning)
static double CalculateArea(double baseLength, double height, bool isTriangle)
{
Console.WriteLine("Calculating triangle area");
return (baseLength * height) / 2;
}
static void Main(string[] args)
{
double square = CalculateArea(5);
Console.WriteLine("Square area: " + square);
Console.WriteLine();
double rectangle = CalculateArea(5, 10);
Console.WriteLine("Rectangle area: " + rectangle);
Console.WriteLine();
double triangle = CalculateArea(6, 8, true);
Console.WriteLine("Triangle area: " + triangle);
}
}
// Output:
// Calculating square area
// Square area: 25
//
// Calculating rectangle area
// Rectangle area: 50
//
// Calculating triangle area
// Triangle area: 24
Output:
Calculating square area Square area: 25 Calculating rectangle area Rectangle area: 50 Calculating triangle area Triangle area: 24
🔹 Overloading Rules and Restrictions
Method overloading must follow specific rules: overloads must differ by parameter type, count, or both—not by return type alone. You cannot overload methods where only the return type varies, as this would cause ambiguity for the compiler. Parameter modifiers like ref and out also contribute to signature differences. Understanding these rules ensures valid, unambiguous overloads that enhance code usability without causing compilation errors or runtime confusion.
Valid Overloading:
- Different parameter count: Add(int a) vs Add(int a, int b)
- Different parameter types: Add(int a) vs Add(double a)
- Different parameter order: Method(int a, string b) vs Method(string a, int b)
Invalid Overloading:
- Return type only: int Add() vs double Add() - NOT allowed
- Parameter names only: Add(int x) vs Add(int y) - NOT allowed
- Access modifiers: public Add() vs private Add() - NOT allowed
class Program
{
// ✓ Valid - different parameter count
static void Print(int x) { }
static void Print(int x, int y) { }
// ✓ Valid - different parameter types
static void Show(int x) { }
static void Show(string x) { }
// ✗ Invalid - only return type differs
// static int Calculate() { }
// static double Calculate() { } // ERROR!
// ✗ Invalid - only parameter names differ
// static void Display(int a) { }
// static void Display(int b) { } // ERROR!
}
🔹 Benefits of Method Overloading
Method overloading improves code readability, reduces complexity, and provides a consistent API experience. Developers can use the same method name for related operations, making libraries easier to learn and use. It eliminates the need for numerous distinct method names (like AddInt, AddDouble), streamlining code maintenance. Overloading also supports polymorphism at compile time, enabling efficient, type-safe calls that adapt to different inputs while keeping the codebase organized and intuitive.
Key Benefits:
- Code readability: One intuitive name instead of multiple similar names
- Flexibility: Same operation works with different data types
- Maintainability: Easier to remember and use consistent naming
- Natural interface: Methods behave as users expect
// Without overloading - confusing!
static void PrintInt(int x) { }
static void PrintDouble(double x) { }
static void PrintString(string x) { }
// With overloading - clean and clear!
static void Print(int x) { }
static void Print(double x) { }
static void Print(string x) { }
// Usage is intuitive
Print(42);
Print(3.14);
Print("Hello");