Перегрузка методов

Перегрузка методов – создание одноименных методов в пределах одного класса, которые отличаются количеством и/или типом параметров. Перегруженные методы могут возвращать значения разных типов данных, однако отличие только в возвращаемом типе не допускается.

Сигнатуры

Сигнатура метода – это часть объявления метода, которая позволяет компилятору идентифицировать метод среди других.

В сигнатуру входят:

  • Имя метода;
  • Количество параметров;
  • Порядок параметров;
  • Тип параметров;
  • Модификаторы параметров.

Названия параметров и тип возвращаемого значения не относится к сигнатуре.

Опираясь на сигнатуру, компилятор выбирает метод, который нужно использовать.

Рассмотрим несколько методов:

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(), универсальный метод, который может принимать разное количество аргументов разного типа данных, при этом поведение его во всех случаях одинакова – вывод значений на экран консольного приложения.

Смотрите также: