1. Einführung
Scope nochmal erklärt
Stell dir vor, Variablen sind Mitarbeiter in einem großen Büro, und Methoden, Schleifen und Codeblöcke sind die Räume und Büros. Manche Mitarbeiter dürfen nur in ihr eigenes Zimmer, andere ins ganze Gebäude. Wo sich wer aufhalten darf – das ist der Scope (scope).
Der Scope bestimmt, wo eine deklarierte Variable im Programm "sichtbar" ist und wo du sie benutzen kannst.
Die wichtigsten Arten von Scopes
In C# gibt es im Wesentlichen diese Scopes:
| Scope | Beispiel | Wo ist die Variable sichtbar? |
|---|---|---|
| Lokal | Innerhalb einer Methode oder eines Blocks | Nur innerhalb dieses Blocks |
| Methodenparameter | In der Methodensignatur | Nur innerhalb der Methode |
| Klassenvariable (Feld) | Im Klassentext außerhalb von Methoden | In allen Methoden dieser Klasse |
| Variable in Schleife/Bedingung | Innerhalb von einer Schleife/if |
Nur innerhalb dieser |
Beispiel mit Erklärungen
public class Office
{
int gebaeudeNummer = 50; // Feld der Klasse: sichtbar in allen Methoden public void PrintInfo() {
int raumNummer = 101; // Lokale Variable: nur in PrintInfo sichtbar if (raumNummer > 100) {
int tischNummer = 5; // Nur in diesem if-Block sichtbar Console.WriteLine(tischNummer);
} Console.WriteLine(tischNummer); //
Fehler! tischNummer ist hier nicht mehr sichtbar
}
}
2. Lokale Funktionen und Scope
Wer sieht wen?
Wenn du eine lokale Funktion innerhalb einer Methode (oder sogar in einer Schleife oder Bedingung) deklarierst, ist sie im selben Scope wie die Variablen, die weiter oben deklariert wurden. Die lokale Funktion ist quasi "Teil dieses Zimmers".
Beispiel
Die lokale Funktion sieht Variablen aus dem umgebenden Scope
void PrintWithPrefix(string nachricht)
{
string
praefix = "[LOG]: "; void Print() {
Console.WriteLine(
praefix +
nachricht); // sieht beide Variablen!
} Print();
}
Hier sind die Variablen praefix und nachricht innerhalb der lokalen Funktion Print sichtbar, weil sie im selben oder in einem weiteren Scope deklariert wurden.
Wie viele Scopes gibt's hier?
Im Beispiel oben:
- gibt es den Scope der Methode PrintWithPrefix
- und darin den Scope der Funktion Print
3. Variable Capture (Capture)
Variable Capture ist der Fall, wenn eine lokale Funktion Variablen benutzt, die außerhalb dieser Funktion, aber im selben Scope deklariert wurden.
Lokale Funktionen und anonyme Methoden (Lambda-Ausdrücke, dazu kommen wir noch) merken sich (oder "capturen") alle Variablen, die beim Deklarieren sichtbar waren.
Man kann sagen, die Funktionen machen einen Snapshot (capture) der Umgebung – und können diese Variablen auch dann noch benutzen, wenn sie viel später aufgerufen werden.
Schematisch
Methode Main
└─ Variable x
└─ lokale Funktion F() ← "captured" x
Beispiel – einfaches Capture
void CounterBeispiel()
{
int zaehler = 0;
void Erhoehen()
{
zaehler++; // Diese Funktion captured die Variable zaehler
}
Erhoehen();
Erhoehen();
Console.WriteLine(zaehler); // Gibt 2 aus
}
Hier wird nach zwei Aufrufen der lokalen Funktion Erhoehen der Wert von zaehler auf 2 erhöht.
4. Anwendung von Variable Capture
Variable Capture macht es easy, Daten zwischen dem Methodenscope und lokalen Funktionen zu "übergeben", ohne sich mit unnötigen Parametern rumzuärgern.
Ohne Capture müsstest du alle Variablen als Parameter übergeben:
void CounterBeispielOhneCapture()
{
int zaehler = 0;
void Erhoehen(ref int c)
{
c++;
}
Erhoehen(ref zaehler);
Erhoehen(ref zaehler);
Console.WriteLine(zaehler);
}
Das ist nervig – warum ständig ref schreiben und die Funktionssignatur verhunzen, wenn die Funktion die Variablen draußen easy sehen kann?
5. Lokale Funktionen und das Leben der Variablen nach dem Methodenende
Leben Variablen länger als die Methode?
Wenn du eine lokale Funktion (oder einen Delegate mit Lambda) irgendwo außerhalb der aktuellen Methode weitergibst, sterben die gecaptureden Variablen nicht automatisch beim Methodenende. Die CLR (die .NET-VM) kümmert sich drum – alles Nötige bleibt im Speicher "am Leben".
Beispiel: Funktionen leben außerhalb der Methode
Func<int> GetCounter()
{
int zaehlen = 0;
int Erhoehen()
{
zaehlen++;
return zaehlen;
}
return Erhoehen; // Wir geben die Funktion nach draußen zurück!
}
var counter = GetCounter();
Console.WriteLine(counter()); // 1
Console.WriteLine(counter()); // 2
Hier lebt die Variable zaehlen auch nach dem Ende der Methode GetCounter weiter, weil die zurückgegebene Funktion sie gecaptured hat. Das nennt man Closure – dazu gibt's noch eine eigene Vorlesung, aber bei lokalen Funktionen läuft das genauso.
6. Typische Fehler und interessante Szenarien
Variable überschreiben vor dem Aufruf der lokalen Funktion
Manchmal passiert es, dass die Variable, die von der lokalen Funktion gecaptured wird, vor deren Aufruf geändert wird – und dann kommt ein anderes Ergebnis raus als erwartet.
Beispiel:
void Beispiel()
{
int x = 42;
void PrintX() { Console.WriteLine(x); }
x = 100; // Variable geändert!
PrintX(); // Gibt 100 aus, nicht 42!
}
Trick: Die lokale Funktion sieht immer den aktuellsten Wert der Variable zum Zeitpunkt des Aufrufs.
Variable Capture in for/foreach-Schleifen (nochmal)
Klassischer Pain: Wenn du Logik im Bewerbungsgespräch oder in einem großen Projekt schreibst, check immer: Capture ich hier eine "lebende" Variable aus der Schleife, und wie verhält sie sich?
GO TO FULL VERSION