Что такое перегрузка методов c
Перейти к содержимому

Что такое перегрузка методов c

Что такое перегрузка методов c

Иногда возникает необходимость создать один и тот же метод, но с разным набором параметров. И в зависимости от имеющихся параметров применять определенную версию метода. Такая возможность еще называется перегрузкой методов (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 и работать не будет .

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

В 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(); >> > 

Использование перегруженного метода C#

Как видите метод 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, если бы C# не поддерживал перегрузки, то пришлось бы присваивать каждому методу свое имя, что конечно же очень неудобно. В данном случае метод IndexOf реализует несколько перегрузок, для поиска символов и подстрок в исходной строке.

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

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

Сигнатуры

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

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

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

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

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

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

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

Используя перегрузку можно увеличить количество аргументов, для нахождения минимального из трех чисел:

static int Min(int n1, int n2, int n3) < //вызов предыдущего метода var m = Min(n1, n2); return m

Вызов перегруженных методов:

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) 

Может показаться, что к этому методу подходят сразу две сигнатуры: ShowSum(byte, byte, byte) и ShowSum(byte, byte), но это не так, подходит только первый вариант. Поэтому если перегрузить его методом с двумя параметрами:

static void ShowSum(byte x, byte y) 

он будет иметь больший приоритет, и аргумент по умолчанию не используется. Выйти из ситуации можно используя именованные параметры:

ShowSum(2, 3); //5, а не 10, как можно ожидать ShowSum(2, 3, 1); //6 ShowSum(a: 2, b: 3); //10 

Хотя при разработке программ, таких конструкций лучше избегать, потому что они вносят неоднозначность.

На практике перегрузки встречаются очень часто. К примеру метод Console.Write(), универсальный метод, который может принимать разное количество аргументов разного типа данных, при этом поведение его во всех случаях одинакова – вывод значений на экран консольного приложения.

Что такое перегрузка методов c

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

В этой статье я доступным языком расскажу о механизме перегрузки методов в C#. О том, что из себя представляет данных механизм, как и когда им пользоваться. Методы в C#, это подпрограммы, которые создаются для многократного выполнения однотипных действий. Часто, программисту приходится сталкиваться с ситуациями, когда необходимо выполнять однотипные действия над разными по типу входными параметрами, или над разным количеством параметров… Представим, что нам нужно написать метод, который принимает два целочисленных аргумента и возвращает их сумму. Выглядеть этот метод может примерно так:

//Получает два целых числа и возвращает их сумму static int Sum(int aFirstArg, int aSecondArg)

И все бы ничего, но нам может понадобиться создать еще один метод, который принимает в качестве параметров не целые числа, а вещественные, но выполняет ту же функцию, или метод, который принимает не два, а три параметра. В таком случае, нам нужно придумать для каждого такого метода свое, уникальное и более или менее соответствующее название! А это уже как минимум, напрягает программиста, который склонен решать какие-то практические задачи, а не выбирать имена методам… Так вот, механизм перегрузки методов избавляет программиста от подобных проблем, а именно, позволяет создавать методы с одним и тем же именем, но с разным списком аргументов. Причем отличаться обязательно должны именно аргументы методов, возвращаемое значение тут не учитывается.

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

Вот пример допустимой перегрузки метода «Sum»:

//Получает два целых числа и возвращает их сумму static int Sum(int aFirstArg, int aSecondArg) < return aFirstArg + aSecondArg; >//Получает три целых числа, и возвращает их сумму static int Sum(int aFirstArg, int aSecondArg, int aThirdArg) < return aFirstArg + aSecondArg + aThirdArg; >//Получает два вещественных числа и возвращает их сумму static double Sum(double aFirstArg, double aSecondArg)

А вот так уже нельзя перегружать тот же метод:

//Получает два целых числа и возвращает их сумму static int Sum(int aFirstArg, int aSecondArg) < return aFirstArg + aSecondArg; >//Так перегружать метод Sum нельзя static long Sum(int aFirstArg, int aSecondArg) < return aFirstArg + aSecondArg; >//Так перегружать метод Sum нельзя static int Sum(int aAgr1, int Arg2)

А всё это по тому, что компилятор «смотрит» на количество и типы аргументов методов (не учитывая имена аргументов и возвращаемое значение). И все это для того, чтобы компилятор однозначно «понимал», какой конкретно метод нужно вызывать в определенной ситуации. Если Вы вызовете метод «Sum», передав туда два целых числа, то вызовется первый метод, а если передадите два вещественных числа, то уже третий.

Перегрузка методов – одна из форм полиморфизма. Полиморфизм – один из основных принципов ООП, хотя и не самая крутая.

Таким образом, программист получает дополнительные возможности при создании методов, но нужно помнить, что иметь возможность – еще не значит обязательно ею пользоваться. Иногда лучше создать отдельный метод со своим уникальным именем, хотя, иногда – это только иногда… Но в любом случае, знать, что такое перегрузка методов и как её использовать нужно непременно!

Добавить комментарий Отменить ответ

Для отправки комментария вам необходимо авторизоваться.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *