CodeGym /Kurslar /C# SELF /Dəyişənlərin local funksiyalar tərəfindən "tutulması"

Dəyişənlərin local funksiyalar tərəfindən "tutulması"

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

1. Giriş

Scope-u xatırlayırıq

Gəlin təsəvvür edək ki, dəyişənlər — böyük bir ofisin işçiləridir, metodlar, dövrələr və kod blokları isə otaqlar və kabinetlərdir. Bəzi işçiləri ancaq öz otağına buraxırlar, digərləri isə ümumiyyətlə bütün binada gəzə bilər. Kim harada ola bilər — bax bu onların scope-u (scope) olur.

Scope müəyyən edir ki, elan olunan dəyişən proqramda harada “görünür” və harada istifadə oluna bilər.

Scope-un əsas növləri

C#-da əsasən belə scope-lar var:

Scope Nümunə Dəyişən harada "görünür"
Lokal Metodun və ya blokun içində Ancaq həmin blokun içində
Metod parametri Metodun signature-ında Ancaq metodun içində
Sinif dəyişəni (field) Sinifin bədənində, metodlardan kənarda Bu sinifin bütün metodlarında
Dövrənin/şərtin içində dəyişən
{}
dövrənin/if-in içində
Ancaq həmin
{}
içində

İzahlı nümunə

public class Office
{
    
  
int buildingNumber = 50; // Sinifin field-i: bütün metodlarda görünür public void PrintInfo() {
int roomNumber = 101; // Lokal dəyişən: ancaq PrintInfo-da görünür if (roomNumber > 100) {
int deskNumber = 5; // Ancaq bu if blokunda görünür Console.WriteLine(deskNumber);
} Console.WriteLine(deskNumber); // Xəta! deskNumber burada artıq görünmür
}
}

2. Lokal funksiyalar və scope

Kim kimi "görür"?

Əgər sən lokal funksiyanı metodun içində (hətta dövrənin və ya şərtin içində) elan edirsənsə, o, yuxarıda elan olunan dəyişənlərlə eyni scope-da olur. Lokal funksiya — sanki “eyni kabinetin bir hissəsidir”.

Nümunə

Lokal funksiya ətraf scope-dakı dəyişənləri görür


    void PrintWithPrefix(string message)
{
    
  
string prefix = "[LOG]: "; void Print() {
Console.WriteLine( prefix + message); // hər iki dəyişəni görür!
} Print();
}

Burada prefixmessage dəyişənləri lokal Print funksiyasının içində görünür, çünki onlar eyni və ya daha geniş scope-da elan olunub.

Burada neçə scope var?

Yuxarıdakı nümunədə:

  • PrintWithPrefix metodunun scope-u var
  • onun içində — Print funksiyasının scope-u var
Lokal funksiyalar tam ayrıca “otaq” yaratmır: onlar koridorda (metodda) olan hər şeyi görə bilir.

3. Dəyişənlərin tutulması (Capture)

Dəyişənlərin tutulması — bu o vəziyyətdir ki, lokal funksiya özündən kənarda, amma eyni scope-da elan olunan dəyişənləri istifadə edir.

Lokal funksiyalar və anonim metodlar (lambda ifadələri, ona bir az sonra baxacağıq) onlara elan olunanda əlçatan olan bütün dəyişənləri yadda saxlayır (və ya “tutur”).

Belə demək olar ki, funksiyalar ətraf aləmin bir snapshot-unu (capture) çəkir — və bu dəyişənləri çox sonralar çağırılanda belə istifadə edə bilir.

Qrafik olaraq

Main metodu
└─ dəyişən x
└─ lokal funksiya F() ← "tutur" x-i

Nümunə — ən sadə capture

void CounterExample()
{
    int counter = 0;

    void Increase()
    {
        counter++; // Bu funksiya counter dəyişənini tutur
    }

    Increase();
    Increase();
    Console.WriteLine(counter); // 2 çıxacaq
}
Lokal funksiya xarici scope-dan dəyişəni tutur

Burada iki dəfə Increase lokal funksiyası çağırılandan sonra counter dəyəri 2 olur.

4. Dəyişənlərin tutulmasının tətbiqi

Dəyişənlərin tutulması imkan verir ki, metodun scope-u ilə lokal funksiyalar arasında məlumatı rahat “ötürəsən”, əlavə parametrlərlə əziyyət çəkmədən.

Əgər capture olmasaydı, bütün dəyişənləri parametr kimi ötürməli olardın:

void CounterExampleWithoutCapture()
{
    int counter = 0;

    void Increase(ref int c)
    {
        c++;
    }

    Increase(ref counter);
    Increase(ref counter);
    Console.WriteLine(counter);
}

Bu narahatdır — niyə hər dəfə ref yazıb funksiyanın signature-ını korlayasan ki, əgər o, rahatlıqla xaricdəki dəyişənləri görə bilirsə?

5. Lokal funksiyalar və dəyişənlərin metoddan sonra yaşaması

Dəyişənlər metoddan sonra yaşayır?

Əgər lokal funksiyanı (və ya lambda ilə delegate-i) cari metoddan kənara ötürürsənsə, tutulan dəyişənlər avtomatik olaraq “ölmür” — CLR (.NET-in virtual maşını) hər şeyi yadda saxlayacaq, lazım olan hər şey yaddaşda “qulaqdan asılı” qalacaq.

Nümunə: funksiyalar metoddan kənarda yaşayır

Func<int> GetCounter()
{
    int count = 0;
    int Increment()
    {
        count++;
        return count;
    }
    return Increment; // Funksiyanı çölə qaytarırıq!
}

var counter = GetCounter();
Console.WriteLine(counter()); // 1
Console.WriteLine(counter()); // 2
Closure: count dəyişəni metoddan sonra da yaşayır

Burada, GetCounter metodu bitəndən sonra belə, count dəyişəni yaşayır, çünki qaytarılan funksiya onu tutub. Buna closure deyilir — bu barədə ayrıca mühazirədə danışacağıq, amma lokal funksiyalar səviyyəsində də eyni mexanizmdir.

6. Tipik səhvlər və maraqlı ssenarilər

Lokal funksiya çağırılmamışdan əvvəl dəyişənin yenilənməsi

Bəzən belə olur ki, lokal funksiya tərəfindən tutulan dəyişən, funksiya çağırılmamışdan əvvəl dəyişir — və nəticə gözlədiyindən fərqli olur.

Nümunə:


void Example()
{
    int x = 42;
    void PrintX() {  Console.WriteLine(x);  } 

    x = 100; // Dəyişəni dəyişdik!
    PrintX(); // 100 çıxacaq, 42 yox!
}

Trik: Lokal funksiya çağırılan anda dəyişənin ən son dəyərini görür.

for/foreach dövrəsində dəyişənlərin tutulması (bir daha)

Klassik ağrı: əgər sən interview-da və ya böyük layihədə kod yazırsansa, həmişə yoxla: görəsən, dövrədən “canlı” dəyişəni tuturam, və o necə davranacaq.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Lokal funksiyanın və dəyişənin tutulmasının sadə istifadəsi
Lokal funksiyanın və dəyişənin tutulmasının sadə istifadəsi
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Rəqəmsal sayğacın lokal funksiya ilə başlanğıcı
Rəqəmsal sayğacın lokal funksiya ilə başlanğıcı
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Kortec-lər və lokal funksiyalar
Kortec-lərin out və anonim tiplərlə müqayisədə üstünlükləri
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION