CodeGym /Kurslar /C# SELF /Metodların overload olunması (Method Overloading)

Metodların overload olunması (Method Overloading)

C# SELF
Səviyyə , Dərs
Mövcuddur

1. Giriş

Həyatda bir çox əməliyyatlar — sanki çoxfunksiyalı isveçrə bıçaqları kimidir: eyni komanda müxtəlif alətlərlə işləyə bilər. Məsələn, təsəvvür elə bankomatı: kartı daxil edirsən — bankomat pin-kod soruşur; telefon nömrəsi daxil edirsən — bankomat SMS-dən təsdiq kodu gözləyir. Əməliyyat bir dənədir — "istifadəçini yoxla", amma üsullar fərqlidir.

Proqramlaşdırmada da tez-tez belə situasiyalar olur: elə bil bir əməliyyat yerinə yetirilməlidir, amma verilənlər fərqli tiplərdə və ya fərqli sayda ola bilər. Məsələn, metodumuz həm insanı, həm də heyvanı salamlamalıdır, ya da iki, üç, hətta on ədəd tam ədədi toplamalıdır.

Əlbəttə, metodları fərqli adlarla adlandıra bilərik: SumTwo, SumThree, SumArray. Amma proqramçılar tənbəldir (boşuna demirlər ki, tənbəllik tərəqqinin mühərrikidir). Üstəlik, belə kod oxunmaz olacaq.

Metodun overload olunması

Metodların overload olunması — bu, eyni adda metodu fərqli parametrlərlə işləməyə "məcbur etmək" üsuludur, metodun adı dəyişmir. Bu, polimorfizmin bir növüdür, amma inheritance ilə bağlı deyil.

Metodun overload olunması — bu, bir class-da (və ya struct-da) eyni adda bir neçə metod yaratmaq imkanıdır, amma parametrlərin siyahısı (tipi, sayı və/və ya ardıcıllığı) fərqli olur.

Metodun signaturası

Signatura — C#-da metodun adı üstəgəl parametrlərin tipi və ardıcıllığıdır. Metodun qaytardığı tip signaturaya daxil deyil! Bu, tez-tez gözlənilməz səhvlərə səbəb olur (bu barədə bir azdan danışacam).

2. Overload işdə: sadə nümunələr

Gəlin Greeter adlı bir class yaradaq, hansı ki, istifadəçiləri müxtəlif cür salamlayacaq: sadəcə adla, ad və yaşla, ya da ümumiyyətlə parametr olmadan.


public class Greeter
{
    // Parametrsiz salamlaşma
    public void Greet()
    {
        Console.WriteLine("Salam, dünya!");
    }

    // Adla salamlaşma
    public void Greet(string name)
    {
        Console.WriteLine($"Salam, {name}!");
    }

    // Ad və yaşla salamlaşma
    public void Greet(string name, int age)
    {
        Console.WriteLine($"Salam, {name}! Sən artıq {age} yaşındasan? Pis deyil!");
    }
}

İndi bu metodlardan istənilən birini çağıra bilərik və C# kompilyatoru özü lazım olan variantı seçəcək — ötürülən parametrlərin sayına və tipinə baxaraq.

var greeter = new Greeter();
greeter.Greet();                // Salam, dünya!
greeter.Greet("Anya");          // Salam, Anya!
greeter.Greet("Pyotr", 23);     // Salam, Pyotr! Sən artıq 23 yaşındasan? Pis deyil!

3. Tipə və parametr sayına görə fərqləndirmə

Overload işləyir, əgər metodlar fərqlənirsə:

  • parametrlərin sayı ilə,
  • heç olmasa bir parametrin tipi ilə,
  • parametrlərin tiplərinin ardıcıllığı ilə (amma burada diqqətli olmaq lazımdır).

Gəlin təkcə yaş qəbul edən bir overload əlavə edək:


public void Greet(int age)
{
    Console.WriteLine($"Bu qədər yaş — superdir! ({age} yaş)");
}

İndi çağırışlar:

greeter.Greet(10);          // Bu qədər yaş — superdir! (10 yaş)

Vacibdir yadında saxla: əgər metodlar yalnız qaytardığı tipə görə fərqlənirsə, onları overload etmək olmaz. Məsələn, belə kod səhv verəcək:


// Kompilyasiya səhvi!
public int Foo(string s) { ... }
public double Foo(string s) { ... }
Yalnız qaytardığı tipə görə overload mümkün deyil!

Kompilyator deyəcək: "Artıq Foo(string) metodu müəyyən olunub, bir az daha çətin bir şey elə!"

4. Overload və C# standart kitabxanası

Overload — təkcə bizim uydurduğumuz Greet deyil. Gəlin .NET üçün Console.WriteLine sənədinə baxaq:

Signatura Təyinatı
WriteLine()
Boş sətri çap edir
WriteLine(string)
Sətir çap edir
WriteLine(int)
Tam ədəd çap edir
WriteLine(double)
Ondalık ədəd çap edir
WriteLine(string, object)
Bir arqumentlə sətri formatlayır
WriteLine(string, params object[])
Birkaç arqumentlə formatlayır

Bunların hamısı eyni metodun overload-larıdır — WriteLine. İndi başa düşürsən ki, niyə həmişə belə yaza bilirsən:

Console.WriteLine("Sadəcə sətir");
Console.WriteLine(123);
Console.WriteLine(2.5);
Console.WriteLine("Cəm: {0}", 42);

Və kompilyator həmişə sənin çağırışını düzgün başa düşəcək!

5. Kompilyator hansı overload-u çağıracağını necə seçir?

Burada hər şey dəqiqdir: o, faktiki ötürülən arqumentlərin tipinə və sayına baxır. Kiçik bir cədvəl:

Çağırış Hansı versiya işləyəcək?
greeter.Greet()
void Greet()
greeter.Greet(75)
void Greet(int)

Bəs qeyri-müəyyənlik olanda nə baş verir?

Bəzən vəziyyət nəzarətdən çıxır. Qeyri-müəyyən overload nümunəsi — kompilyator düzgün versiyanı seçə bilmir


public void Print(int a, double b) { ... }
public void Print(double a, int b) { ... }

printer.Print(5, 10); 
// Səhv: qeyri-müəyyənlik — hansı Print çağırılsın? (hər ikisi uyğun gəlir kimi)

Kompilyator qeyri-müəyyənlik səhvi verəcək. Belə hallarda, kompilyatoru çaşdıra biləcək eyni sayda və oxşar tipli parametrlərlə overload-lardan qaçmaq daha yaxşıdır.

6. params — dəyişkən sayda parametrlər

Tutaq ki, sən elə bir metod yazmaq istəyirsən ki, qeyri-müəyyən sayda ədəd qəbul etsin. Burada sənə params açar sözü kömək edəcək.


public void SumAll(params int[] numbers)
{
    int sum = 0;
    foreach (int n in numbers)
        sum += n;
    Console.WriteLine($"Cəm: {sum}");
}
Dəyişkən sayda parametrlə metod ( params)

İndi belə çağırmaq olar:

SumAll(1, 2, 3);           // Cəm: 6
SumAll(10, 20);            // Cəm: 30
SumAll();                  // Cəm: 0

params ilə metodları overload ilə birləşdirmək olar, amma əsas odur ki, kompilyatorun hansı versiyanı nəzərdə tutduğunu dəqiq başa düşməsinə mane olan overload-lar etməyəsən.

7. Overload və parametr modifikatorları (ref, out, in)

C# metodları parametr modifikatorlarına görə fərqləndirir (yəni, void Foo(int a) ilə void Foo(ref int a) signaturası fərqlidir və hər ikisi bir class-da ola bilər):

public void SetValue(int a)
{
    a = 42;
}

public void SetValue(ref int a)
{
    a = 100;
}
ref modifikatoruna görə overload

ref olmadan çağırış birinci versiyaya düşəcək, ref ilə isə ikinciyə:

int n = 5;
SetValue(n);     // n olduğu kimi qalır (dəyər kopyalanır)
SetValue(ref n); // n olur 100

8. Sxem: overload nədir


      +----------+
      |  MyClass |
      +----------+
           |
           |            (metodların fraqmenti)
    +-----------------------+
    |   void Foo()          |
    |   void Foo(int a)     |
    |   void Foo(string s)  |
    |   void Foo(int a, int b) |
    +-----------------------+
Class-da metodların overload sxemi

Koddakı kimi təsəvvür etsək:

// Foo() metodunun overload versiyalarını çağırırıq:
var mc = new MyClass();
mc.Foo();              // void Foo()
mc.Foo(5);             // void Foo(int)
mc.Foo("Hello");       // void Foo(string)
mc.Foo(2, 3);          // void Foo(int, int)

9. Nümunə: tətbiqimizdə metodları overload edirik

Gəlin tədris tətbiqimizi inkişaf etdirək, heyvanlar iyerarxiyasına metod overload əlavə edək.


public class Animal
{
    public string Name { get; set; }

    // Səs çıxarmaq üçün metod
    public virtual void MakeSound()
    {
        Console.WriteLine("Naməlum bir səs...");
    }

    // Overload olunmuş metod: səsin səviyyəsi ilə
    public void MakeSound(int volume)
    {
        Console.WriteLine($"Heyvan {volume} dB səviyyəsində səs çıxarır.");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Hav!");
    }

    // Overload olunmuş metod: havlama səsi ilə səviyyə
    public void MakeSound(int volume)
    {
        Console.WriteLine($"Hav! (səviyyə: {volume} dB)");
    }
}

Belə çağırışları yoxla:

Dog rex = new Dog();
rex.MakeSound();           // Hav!
rex.MakeSound(75);         // Hav! (səviyyə: 75 dB)

Diqqət yetir: child class-da (Dog) biz MakeSound(int volume) metodunu overload etdik və indi hər iki versiya var: parametrli və parametrsiz.

10. Metodların overload olunmasında tipik səhvlər

Səhv №1: yalnız qaytardığı tipə görə overload etməyə cəhd.
Bu mümkün deyil — qaytarılan tip metodun signaturasına daxil deyil. Overload fərqli sayda və ya tipdə giriş parametrləri ilə olmalıdır, void və ya int ilə yox.

Səhv №2: kompilyatoru çaşdıran qeyri-müəyyən overload-lar.
Eyni sayda və oxşar tipli overload-lar (məsələn, intdouble) kompilyatoru çaşdıra bilər. Nümunə: Print(int a, double b)Print(double a, int b)Print(1, 1) çağırışı qeyri-müəyyənlik səhvi verəcək.

Səhv №3: params ilə digər overload-ların konflikti.
params ilə metod başqa overload üçün nəzərdə tutulmuş çağırışı "ələ keçirə" bilər. Əgər tiplər üst-üstə düşürsə, kompilyator gözləmədiyin metodu seçə bilər.

Səhv №4: refout signaturaya daxil olduğunu unutmaq.
Do(ref int x)Do(out int x) metodları fərqli overload sayılır. Bunu nəzərə almasan, asanlıqla səhv versiyanı çağıra bilərsən.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION