Перевантаження методів

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

Сигнатури

Сигнатура методу – це частина оголошення методу, яка дозволяє компілятору ідентифікувати метод серед інших.

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

  • Ім’я методу;
  • Кількість параметрів;
  • Порядок параметрів;
  • Тип параметрів;
  • Модифікатори параметрів.

Назви параметрів та тип значення яке повертається до сигнатури не входять.

Спираючись на сигнатуру, компілятор вибирає метод, який необхідно використовувати.

Розглянемо декілька методів:

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

Дивіться також: