1. Einführung
In C# werden standardmäßig alle Methodenparameter by value übergeben, das heißt, die Methode bekommt eine Kopie, nicht das Original.
Aber was, wenn du eine externe Variable innerhalb einer Methode ändern willst? Oder mehrere Werte aus einer Methode zurückgeben möchtest? Oder vielleicht willst du eine große Struktur an eine Methode übergeben, aber nicht den ganzen "Koffer ohne Griff" kopieren, sondern einfach nur eine Referenz übergeben, um Speicher zu sparen?
Genau darum geht's bei den Parameter-Modifiern. Sie geben dir die Kontrolle über den "Daten-Übergabeweg" zwischen aufrufendem Code und Methode.
Arten von Modifikatoren
| Modifikator | Datenübergabe | Muss vor dem Aufruf initialisiert werden? | Darf man innen lesen? | Darf man innen schreiben? | Typische Anwendungsfälle |
|---|---|---|---|---|---|
| ref | Beidseitig | Ja | Ja | Ja | Variable zum Lesen und/oder Ändern übergeben |
| out | Nur nach außen | Nein | Nein (bis zur Zuweisung) | Ja (Pflicht!) | Mehrere Werte aus einer Methode zurückgeben |
| in | Nur nach innen | Ja | Ja | Nein | Große Strukturen "read only" und speichersparend übergeben |
Keine Sorge, wir gehen jetzt alle durch. Ist echt einfacher als es aussieht.
2. Modifikator ref: Per Referenz übergeben: lesen und schreiben
Stell dir vor: Du bringst deinem Kumpel eine Tasse Kaffee und sagst – "Hier, kannst du trinken, aufwärmen oder sogar was reinkippen". Dein Kumpel kann mit dem Kaffee machen, was er will, und was übrig bleibt (oder nicht) – bekommst du zurück. So funktioniert ref.
void DoSomething(ref int x)
{
x = x + 10; // Wir ändern den Eingabeparameter
}
Um einen Parameter als ref zu übergeben, musst du ihn sowohl bei der Deklaration als auch beim Methodenaufruf mit diesem Modifikator versehen:
int myNumber = 5;
DoSomething(ref myNumber);
Console.WriteLine(myNumber); // gibt 15 aus
Es muss zwingend eine Variable sein, du kannst keinen Ausdruck per Referenz übergeben. So ein Code funktioniert nicht:
DoSomething(ref 10); // hier muss eine Variable hin!
- Vor dem Aufruf: Die Variable muss initialisiert sein (muss einen Wert haben).
- Innerhalb der Methode: Lesen und Ändern ist erlaubt.
- Nach dem Aufruf: Änderungen bleiben erhalten.
Beispiel: Werte von Variablen tauschen
Manchmal will man die Werte von zwei Variablen tauschen. Aber Achtung: In C# werden Value Types (wie int) standardmäßig by value übergeben. Das heißt, beim Methodenaufruf werden die Werte kopiert, nicht die Referenzen auf die Variablen. Damit die Methode die Originalvariablen ändern kann, brauchst du das Schlüsselwort ref.
Beispiel ohne ref – funktioniert nicht:
// Versuch zu tauschen – klappt nicht
void Swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 10;
int y = 20;
Swap(x, y); // Es werden Wertkopien übergeben
Console.WriteLine($"{x}, {y}"); // → 10, 20 – nichts hat sich geändert!
Innerhalb der Funktion Swap sind die Variablen a und b Kopien von x und y. Wir ändern nur die Kopien, die Originale bleiben wie sie sind.
Beispiel mit ref – Variablen werden getauscht
// Werte von Variablen mit ref tauschen
void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 10;
int y = 20;
Swap(ref x, ref y); // Referenzen auf Variablen werden übergeben
Console.WriteLine($"{x}, {y}"); // → 20, 10 – alles richtig getauscht
ref erlaubt es, Referenzen auf die Variablen selbst zu übergeben, nicht nur deren Werte.
Deshalb arbeitet die Funktion Swap direkt mit x und y und ändert deren Inhalt.
Wann ref verwenden?
- Wenn du die übergebene Variable ändern willst (z.B. erhöhen, ersetzen).
- Wenn du keinen neuen Wert zurückgeben willst (wie mit return), sondern das Argument direkt ändern möchtest.
- Nicht verwenden, um zig Werte "rauszuschieben" – dafür gibt's einen anderen Modifikator.
3. Modifikator out: Nach außen übergeben
Jetzt stell dir vor: Du kommst zu deinem Kumpel, hast aber eine leere Tasse und sagst: "Füll die Tasse – egal womit, Kaffee oder Tee!" Danach muss dein Kumpel sie mit irgendwas füllen, sonst meckert der Compiler.
out ist dafür da, dass eine Methode mehrere verschiedene Werte zurückgeben kann, ohne das Rückgabewert-Feld zu benutzen. Solche Parameter müssen zwingend innerhalb der Methode initialisiert werden.
void GetCoordinates(out int x, out int y)
{
x = 5;
y = 10; // Ohne das – Fehler!
}
- Vor dem Aufruf: Die Variable muss nicht initialisiert sein, du kannst sogar einfach out int x im Aufruf schreiben.
- Innerhalb der Methode: Wert muss zwingend vor dem Verlassen der Methode zugewiesen werden, sonst streikt der Compiler.
- Nach dem Aufruf: Die Variable enthält den neuen Wert.
Beispiel: Zwei Werte aus einer Methode zurückgeben
void ParseNameAndAge(string input, out string name, out int age)
{
string[] parts = input.Split(','); // String in Array splitten – teilen nach ','
name = parts[0];
age = int.Parse(parts[1]);
}
string userInput = "Ivan,25";
ParseNameAndAge(userInput, out string userName, out int userAge);
Console.WriteLine($"{userName} — {userAge} Jahre alt");
Wann out verwenden?
- Wenn du mehrere Werte aus einer Methode zurückgeben willst (z.B. einen String in Teile zerlegen, wie oben).
- Wenn der Rückgabetyp vorher nicht feststeht (z.B. Versuch, einen String in eine Zahl zu konvertieren).
Beispiele aus .NET: Die Methode int.TryParse(string, out int) – ein Klassiker!
string input = "123";
if (int.TryParse(input, out int parsedNumber))
{
Console.WriteLine($"Konvertiert: {parsedNumber}");
}
else
{
Console.WriteLine("Konvertierungsfehler!");
}
Die Methode int.TryParse gibt true zurück, wenn die Konvertierung des Strings in eine Zahl geklappt hat, und false, wenn nicht. Den Zahlenwert selbst gibt sie über out zurück.
Typische Fehler und lustige Situationen mit ref und out
- Variable für ref nicht initialisiert – dann ist der Compiler sauer.
- Wert für out nicht zugewiesen – Compiler noch wütender.
- Modifikator beim Aufruf vergessen: DoSomething(x) statt DoSomething(ref x) geschrieben.
- ref oder out mit Konstanten oder Literalen zu verwenden ist verboten! Nur Variablen haben eine Adresse im Speicher, auf die man eine Referenz bilden kann.
4. Modifikator in: Nur lesen, und nur per Referenz
Manchmal muss man einer Methode ein großes und komplexes Objekt übergeben. Normalerweise wird so ein Objekt per Referenz übergeben, aber dann kann die Funktion es versehentlich ändern, weil sie vollen Zugriff hat – schließlich hast du ihr die Referenz gegeben.
Theoretisch könnte man eine komplette Kopie des Objekts machen und übergeben. Dann bleibt das Original unangetastet. Aber wenn das Objekt riesig ist, kopierst du unnötig viele Daten. Viel besser wäre es, das Objekt an die Funktion zu übergeben und den Compiler darauf achten zu lassen, dass es niemand ändert.
in – ein neuer Modifikator, mit dem du Strukturen per Referenz, aber nur zum Lesen übergeben kannst. Das ist wie ein "wertvolles Schwert hinter Glas": anschauen ja, anfassen nein.
void PrintPoint(in Point pt)
{
pt.X = 5; // Fehler! Nur lesen erlaubt.
Console.WriteLine($"Punkt: {pt.X}, {pt.Y}"); //Das geht
}
- Vor dem Aufruf: Die Variable muss initialisiert sein.
- Innerhalb der Methode: Nur lesen, nicht ändern.
- Speicher sparen: Die Struktur wird nicht kopiert, sondern per Referenz übergeben.
Wird z.B. bei Arrays großer Strukturen verwendet, um unnötige Kopien zu vermeiden. Bei Klassen (Reference Types) bringt in aber nicht viel.
Beispiel: Große Struktur per Referenz übergeben, ohne zu kopieren
struct BigData
{
public int A, B, C, D, E;
}
void PrintBigData(in BigData data)
{
data.A = 10; // geht nicht – nur lesen!
Console.WriteLine(data.A + data.B + data.C + data.D + data.E);
}
BigData myData = new BigData { A = 10, B = 20, C = 30, D = 40, E = 50 };
PrintBigData(in myData);
5. Häufig gestellte Fragen:
Kann man ref/out/in mit Arrays und Reference Types verwenden?
Ja, klar! Aber denk dran: Arrays sind schon Reference Types. Wenn du ein Array mit ref übergibst, kannst du die Referenz selbst ändern. Meistens braucht man das aber bei Strukturen.
Was ist der Unterschied zwischen ref und out, wenn ich doch in beiden Fällen einen Wert bekomme?
Bei ref muss die Variable vor der Benutzung initialisiert sein. Bei out nicht, aber innerhalb der Methode MUSS sie mindestens einmal zugewiesen werden.
Was passiert, wenn ich einen Literal (z.B. die Zahl 5) mit ref/out/in verwenden will?
Der Compiler meckert sofort. Nur Variablen sind erlaubt!
Wie viele Parameter kann ich als ref/out/in machen?
So viele du willst, es gibt kein Limit! Aber übertreib's nicht, damit der Code lesbar bleibt: Wenn du mehr als zwei oder drei hast, denk lieber über ein Tuple, eine Struktur oder eine Klasse nach.
GO TO FULL VERSION