Перегрузка методов – создание одноименных методов в пределах одного класса, которые отличаются количеством и/или типом параметров. Перегруженные методы могут возвращать значения разных типов данных, однако отличие только в возвращаемом типе не допускается.
Сигнатуры
Сигнатура метода – это часть объявления метода, которая позволяет компилятору идентифицировать метод среди других.
В сигнатуру входят:
- Имя метода;
- Количество параметров;
- Порядок параметров;
- Тип параметров;
- Модификаторы параметров.
Названия параметров и тип возвращаемого значения не относится к сигнатуре.
Опираясь на сигнатуру, компилятор выбирает метод, который нужно использовать.
Рассмотрим несколько методов:
int Div(int a, int b)
{
return a / b;
}
uint Sum(uint x, uint y, uint z)
{
return x + y + z;
}
Метод Div имеет следующую сигнатуру – Div(int, int), а метод Sum – Sum(uint, uint, uint).
Перегрузка
Исходя из понятия сигнатуры, перегруженными называют методы, которые отличаются сигнатурами, но при этом имеют одинаковые имена.
Пример перегрузки:
public int Mult(int a, int b)
{
return a * b;
}
public double Mult(double x, double y)
{
return x * y;
}
public double Mult(double x, double y, double z)
{
//вызывает предыдущий метод
return Mult(x, y) * z;
}
public string Mult(string s, uint k)
{
var retVal = string.Empty;
for (var i = 0; i < k; i++)
{
retVal += s;
}
return retVal;
}
Как можно заметить, в каждом из рассмотренных примеров использована уникальная сигнатура.
C# также поддерживает сокращенную запись перегруженных методов:
float F(float x) => x - 2 / x;
int F(int x) => x - 2 / x;
При вызове метода с использованием литералов, можно указывать их тип с помощью суффиксов:
var r1 = F(3); //3
var r2 = F(3f); //2.33
Перегруженные методы могут отличаться только модификаторами:
int PlusOne(int i)
{
return i + 1;
}
int PlusOne(ref int i)
{
i++;
return i;
}
Для чего использовать перегрузку методов
Перегрузка используется для создания универсальных методов, логика поведения которых одинакова, но типы данных или количество аргументов разное. Это дает возможность писать красивый код, группируя методы с одинаковым поведением по имени.
Рассмотрим пример поиска минимального значения из двух целых чисел:
static int Min(int n1, int n2)
{
return n1 < n2 ? n1 : n2;
}
Используя перегрузку можно увеличить количество аргументов, для нахождения минимального из трех чисел:
static int Min(int n1, int n2, int n3)
{
//вызов предыдущего метода
var m = Min(n1, n2);
return m < n3 ? m : n3;
}
Вызов перегруженных методов:
static void Main(string[] args)
{
Console.WriteLine(Min(3, -2));
Console.WriteLine(Min(6, 4, 2));
Console.ReadLine();
}
Ограничения при перегрузке методов
Локальные функции
C# не поддерживает перегрузку локальных функций, поэтому такой код не скомпилируется:
public void MyMethod()
{
string Hello(string name) => "Hi! " + name;
string Hello(string firstName, string lastName) => "Hello! " + firstName + " " + lastName;
Console.WriteLine(Hello("John"));
Console.WriteLine(Hello("James", "Smith"));
}
Отличие только по возвращаемому типу
Нельзя перегружать методы, если они отличаются только по типу возвращаемого значения.
Следующий код не скомпилируется:
void DisplayNumber(long l)
{
Console.Write(l);
}
long DisplayNumber(long l)
{
Console.WriteLine(l);
return l;
}
Методы с опциональными параметрами
Рассмотрим метод с параметрами по умолчанию:
static void ShowSum(byte a, byte b, byte c = 5)
{
Console.WriteLine(a + b + c);
}
Может показаться, что к этому методу подходят сразу две сигнатуры: ShowSum(byte, byte, byte) и ShowSum(byte, byte), но это не так, подходит только первый вариант. Поэтому если перегрузить его методом с двумя параметрами:
static void ShowSum(byte x, byte y)
{
Console.WriteLine(x + y);
}
он будет иметь больший приоритет, и аргумент по умолчанию не используется. Выйти из ситуации можно используя именованные параметры:
ShowSum(2, 3); //5, а не 10, как можно ожидать
ShowSum(2, 3, 1); //6
ShowSum(a: 2, b: 3); //10
Хотя при разработке программ, таких конструкций лучше избегать, потому что они вносят неоднозначность.
На практике перегрузки встречаются очень часто. К примеру метод Console.Write(), универсальный метод, который может принимать разное количество аргументов разного типа данных, при этом поведение его во всех случаях одинакова – вывод значений на экран консольного приложения.