Что такое перегрузка методов
В программе мы можем использовать методы с одним и тем же именем, но с разными типами и/или количеством параметров. Такой механизм называется перегрузкой методов (method overloading).
public class Program < public static void main(String[] args) < System.out.println(sum(2, 3)); // 5 System.out.println(sum(4.5, 3.2)); // 7.7 System.out.println(sum(4, 3, 7)); // 14 >static int sum(int x, int y) < return x + y; >static double sum(double x, double y) < return x + y; >static int sum(int x, int y, int z) < return x + y + z; >>
Здесь определено три варианта или три перегрузки метода sum() , но при его вызове в зависимости от типа и количества передаваемых параметров система выберет именно ту версию, которая наиболее подходит.
Стоит отметить, что на перегрузку методов влияют количество и типы параметров. Однако различие в типе возвращаемого значения для перегрузки не имеют никакого значения. Например, в следующем случае методы различаются по типу возвращаемого значения:
public class Program < public static void main(String[] args) < System.out.println(sum(2, 3)); System.out.println(sum(4, 3)); >static int sum(int x, int y) < return x + y; >static double sum(int x, int y) < return x + y; >>
Однако перегрузкой это не будет считаться. Более того такая программа некорректна и попросту не скомилируется, так как метод с одним и тем же количеством и типом параметров определен несколько раз.
Перегрузка методов
В C# допускается совместное использование одного и того же имени двумя или более методами одного и того же класса, при условии, что их параметры объявляются по-разному. В этом случае говорят, что методы перегружаются, а сам процесс называется . Перегрузка методов относится к одному из способов реализации полиморфизма в C#.
В общем, для перегрузки метода достаточно объявить разные его варианты, а об остальном позаботится компилятор. Но при этом необходимо соблюсти следующее важное условие: тип или число параметров у каждого метода должны быть разными.
Совершенно недостаточно, чтобы два метода отличались только типами возвращаемых значений. Они должны также отличаться типами или числом своих параметров. (Во всяком случае, типы возвращаемых значений дают недостаточно сведений компилятору C#, чтобы решить, какой именно метод следует использовать.) Разумеется, перегружаемые методы могут отличаться и типами возвращаемых значений. Когда вызывается перегружаемый метод, то выполняется тот его вариант, параметры которого соответствуют (по типу и числу) передаваемым аргументам.
Давайте рассмотрим пример использования перегрузки методов:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class UserInfo < // Перегружаем метод ui public void ui() < Console.WriteLine("Пустой метод\n"); >public void ui(string Name) < Console.WriteLine("Имя пользователя: ",Name); > public void ui(string Name, string Family) < Console.WriteLine("Имя пользователя: \nФамилия пользователя: ",Name,Family); > public void ui(string Name, string Family, byte Age) < Console.WriteLine("Имя пользователя: \nФамилия пользователя: \nВозраст: ", Name, Family, Age); > > class Program < static void Main(string[] args) < UserInfo user1 = new UserInfo(); // Разные реализации вызова перегружаемого метода user1.ui(); user1.ui("Ерохин", "Александр", 26); Console.ReadLine(); >> >
Как видите метод ui перегружается три раза. Модификаторы параметров ref и out также учитываются, когда принимается решение о перегрузке метода. Несмотря на то что модификаторы параметров ref и out учитываются, когда принимается решение о перегрузке метода, отличие между ними не столь существенно. Давайте добавим еще одну перегрузку в вышеуказанный пример:
// Используем модификатор параметров public void ui(string Name, string Family, ref byte Age)
Перегрузка методов поддерживает свойство полиморфизма, поскольку именно таким способом в C# реализуется главный принцип полиморфизма: один интерфейс — множество методов. Для того чтобы стало понятнее, как это делается, обратимся к конкретному примеру. В языках программирования, не поддерживающих перегрузку методов, каждому методу должно быть присвоено уникальное имя. Но в программировании зачастую возникает потребность реализовать по сути один и тот же метод для обработки разных типов данных.
Допустим, что требуется функция, определяющая абсолютное значение. В языках, не поддерживающих перегрузку методов, обычно приходится создавать три или более вариантов такой функции с несколько отличающимися, но все же разными именами. Например, в С функция abs() возвращает абсолютное значение целого числа, функция labs() — абсолютное значение длинного целого числа, а функция fabs () — абсолютное значение числа с плавающей точкой обычной (одинарной) точности.
В С перегрузка не поддерживается, и поэтому у каждой функции должно быть свое, особое имя, несмотря на то, что все упомянутые выше функции, по существу, делают одно и то же — определяют абсолютное значение. Но это принципиально усложняет положение, поскольку приходится помнить имена всех трех функций, хотя они реализованы по одному и тому же основному принципу. Подобные затруднения в C# не возникают, поскольку каждому методу, определяющему абсолютное значение, может быть присвоено одно и то же имя. И действительно, в состав библиотеки классов для среды .NET Framework входит метод Abs(), который перегружается в классе System.Math для обработки данных разных числовых типов. Компилятор C# сам определяет, какой именно вариант метода Abs() следует вызывать, исходя из типа передаваемого аргумента.
В C# определено понятие сигнатуры, обозначающее имя метода и список его параметров; Применительно к перегрузке это понятие означает, что в одном классе не должно существовать двух методов с одной и той же сигнатурой. Следует подчеркнуть, что в сигнатуру не входит тип возвращаемого значения, поскольку он не учитывается, когда компилятор C# принимает решение о перегрузке метода. В сигнатуру не входит также модификатор params.
Чтобы закрепить понятие перегрузки методов, давайте рассмотрим перегрузку встроенного метода IndexOf класса String пространства имен System:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class Program < static void Main(string[] args) < string s = "Всем привет, это сайт professorweb.ru :)"; char ch = 'е'; string smile = ":)"; Console.WriteLine("Исходная строка: \n\n----------------------\n",s); // Первая перегрузка if (s.IndexOf(ch) != -1) Console.WriteLine("Символ '' находится на позиции ",ch,s.IndexOf(ch)); // Вторая перегрузка if (s.IndexOf(ch, s.IndexOf(ch)+1) != -1) Console.WriteLine("Далее, этот символ встречается на позиции ", s.IndexOf(ch, s.IndexOf(ch) + 1)); // Третья перегрузка if (s.IndexOf(smile, 0, s.Length) != -1) Console.WriteLine("Смайл найден на позиции ", smile, s.IndexOf(smile, 0, s.Length)); // Четвертая перегрузка if (s.IndexOf(smile, StringComparison.Ordinal) != -1) Console.WriteLine("Теперь смайл найден другим способом"); Console.ReadLine(); > > >
В данном примере используется только часть доступных перегрузок метода IndexOf, если бы C# не поддерживал перегрузки, то пришлось бы присваивать каждому методу свое имя, что конечно же очень неудобно. В данном случае метод IndexOf реализует несколько перегрузок, для поиска символов и подстрок в исходной строке.
Что такое перегрузка методов
Иногда возникает необходимость создать один и тот же метод, но с разным набором параметров. И в зависимости от имеющихся параметров применять определенную версию метода. Такая возможность еще называется перегрузкой методов (method overloading).
И в языке C# мы можем создавать в классе несколько методов с одним и тем же именем, но разной сигнатурой. Что такое сигнатура? Сигнатура складывается из следующих аспектов:
- Имя метода
- Количество параметров
- Типы параметров
- Порядок параметров
- Модификаторы параметров
Но названия параметров в сигнатуру НЕ входят. Например, возьмем следующий метод:
public int Sum(int x, int y)
У данного метода сигнатура будет выглядеть так: Sum(int, int)
И перегрузка метода как раз заключается в том, что методы имеют разную сигнатуру, в которой совпадает только название метода. То есть методы должны отличаться по:
- Количеству параметров
- Типу параметров
- Порядку параметров
- Модификаторам параметров
Например, пусть у нас есть следующий класс:
class Calculator < public void Add(int a, int b) < int result = a + b; Console.WriteLine($"Result is "); > public void Add(int a, int b, int c) < int result = a + b + c; Console.WriteLine($"Result is "); > public int Add(int a, int b, int c, int d) < int result = a + b + c + d; Console.WriteLine($"Result is "); return result; > public void Add(double a, double b) < double result = a + b; Console.WriteLine($"Result is "); > >
Здесь представлены четыре разных версии метода Add, то есть определены четыре перегрузки данного метода.
Первые три версии метода отличаются по количеству параметров. Четвертая версия совпадает с первой по количеству параметров, но отличается по их типу. При этом достаточно, чтобы хотя бы один параметр отличался по типу. Поэтому это тоже допустимая перегрузка метода Add.
То есть мы можем представить сигнатуры данных методов следующим образом:
Add(int, int) Add(int, int, int) Add(int, int, int, int) Add(double, double)
После определения перегруженных версий мы можем использовать их в программе:
Calculator calc = new Calculator(); calc.Add(1, 2); // 3 calc.Add(1, 2, 3); // 6 calc.Add(1, 2, 3, 4); // 10 calc.Add(1.4, 2.5); // 3.9
Result is 3 Result is 6 Result is 10 Result is 3.9
Также перегружаемые методы могут отличаться по используемым модификаторам. Например:
void Increment(ref int val) < val++; Console.WriteLine(val); >void Increment(int val)
В данном случае обе версии метода Increment имеют одинаковый набор параметров одинакового типа, однако в первом случае параметр имеет модификатор ref. Поэтому обе версии метода будут корректными перегрузками метода Increment.
А отличие методов по возвращаемому типу или по имени параметров не является основанием для перегрузки. Например, возьмем следующий набор методов:
int Sum(int x, int y) < return x + y; >int Sum(int number1, int number2) < return number1 + number2; >void Sum(int x, int y)
Сигнатура у всех этих методов будет совпадать:
Sum(int, int)
Поэтому данный набор методов не представляет корректные перегрузки метода Sum и работать не будет .
Перегрузка методов
— Привет, Амиго! А я расскажу тебе о перегрузке методов.
— Их еще и перегружать можно?! Да что за день сегодня такой!
— С ними много чего можно делать, но не будем об этом.
— Перегрузка – это очень простая операция. На самом деле – это даже не операция над методами, хотя иногда ее называют страшным словом – параметрический полиморфизм.
Дело в том, что все методы внутри класса должны иметь уникальные имена.
— Да, я знаю об этом.
— Так вот, это не совсем так. Вернее совсем не так. Метод не должен иметь уникальное имя. Уникальным должно быть объединение из имени и типов параметров этого метода. Их еще называют сигнатурами методов.
public void print(); public void print2();
public void print(); public void print(int n);
public void print(int n, int n2); public void print(int n);
public int print(int a); public void print(int n);
public int print(int a, long b); public long print(long b, int a);
— Где-то я уже такое видел.
— Ага. Когда ты пишешь System.out.println, Intellij IDEA подсказывает тебе и выдает в подсказке пару десятков методов print с разными параметрами. Компилятор просто определит нужный метод, по типам переменных, которые ты туда передаешь, и пропишет вызов именно его.
— Это, вроде, не сложно. Не полиморфизм, однако.
— Точнее — не переопределение методов
Кстати, обращаю твое внимание, что имена параметров роли не играют – они теряются при компиляции. После компиляции о методе известно только его имя и типы параметров.