Eine Methode ist ein benannter Codeblock, der Eingaben (Parameter) annimmt, seine Arbeit erledigt und ein Ergebnis zurückgeben kann (den Rückgabewert). In C# steht jede Methode in einem Typ; freistehende Funktionen gibt es nicht.
Ausgabe:
Headphones 48.00
Cable 11.40
Aufbau einer Methode
public static decimal PriceWithTax(decimal net, decimal taxRate)
{
return net * (1 + taxRate);
}
- Zugriffsmodifizierer (
public,private, ...): wer sie aufrufen darf. Fehlt er, ist eine Methodeprivatefür ihren Typ. static: Die Methode gehört zum Typ, nicht zu einem Objekt. Mehr dazu unten.- Rückgabetyp: der Typ des zurückgegebenen Werts oder
voidfür keinen. - Name: per Konvention PascalCase, meist ein Verb (
Calculate,SendEmail,TryParse). - Parameterliste: null oder mehr Paare aus
type name. Jeder Parameter ist eine lokale Variable, die mit dem Argument des Aufrufers initialisiert wird. - Rumpf: die Anweisungen zwischen den geschweiften Klammern.
Der Name plus die Parametertypen (und etwaige Modifizierer ref, out oder in) bilden die Signatur der Methode. Der Rückgabetyp gehört nicht dazu, was bei der Überladung wichtig ist.
Rückgabewerte und void
return beendet die Methode sofort und gibt einen Wert zurück. Eine Methode mit Rückgabetyp muss auf jedem Pfad einen Wert dieses Typs zurückgeben:
static string Grade(int score)
{
if (score >= 50)
return "pass";
} // error CS0161: 'Program.Grade(int)': not all code paths return a value
Der Compiler prüft jeden Zweig. Ergänze das fehlende return "fail"; oder ein throw, wenn das Erreichen dieser Stelle ein Bug ist.
Eine void-Methode gibt nichts zurück. Sie kann trotzdem ein nacktes return; verwenden, um früh auszusteigen:
Ausgabe:
ana@mail.com: newsletter sent
ben@mail.com: not subscribed, skipped
False
True
Methoden mit Ausdruckskörper
Besteht der Rumpf einer Methode aus einem einzigen Ausdruck, ersetzt => die Klammern und das return:
static decimal Discount(decimal price) => price * 0.1m;
static bool IsAdult(int age) => age >= 18;
static void Log(string message) => Console.WriteLine($"[log] {message}");
Das ist dieselbe Methode, kürzer geschrieben. Es funktioniert auch für void-Methoden, solange der Rumpf ein Ausdruck ist (ein Aufruf, eine Zuweisung).
Parameter sind Kopien
Argumente werden standardmäßig als Wert übergeben: Der Parameter bekommt eine Kopie des Arguments. Eine Zuweisung an den Parameter innerhalb der Methode wirkt sich nicht auf die Variable des Aufrufers aus:
Ausgabe:
inside: 42
after: 21
scores: 70, 85, 100
Die List hat sich geändert, weil bei einem Referenztyp der kopierte Wert die Referenz ist: Methode und Aufrufer zeigen auf dasselbe Listenobjekt. scores = new List<int>() innerhalb der Methode würde den Aufrufer nicht betreffen. Damit eine Methode der Variable des Aufrufers etwas zuweisen kann, nimm ref oder out; um Parametern Standardwerte zu geben und sie per Namen aufzurufen, nimm optionale Parameter und benannte Argumente.
Mehrere Werte als Tupel zurückgeben
Seit C# 7.0 kann eine Methode ein Tupel mit benannten Elementen zurückgeben, und der Aufrufer kann es in einzelne Variablen dekonstruieren:
Ausgabe:
min 15, max 27, avg 21.0
range 12
_ verwirft das Element, das du nicht brauchst. Für Ergebnisse, die an vielen Stellen verwendet werden, dokumentiert eine kleine Klasse oder ein Record mit benannten Properties die Form besser als ein Tupel.
Überladung
Mehrere Methoden können sich einen Namen teilen, wenn sich ihre Parameterlisten in Anzahl, Typen oder Reihenfolge der Typen unterscheiden. Der Compiler wählt für jeden Aufruf die beste Übereinstimmung:
Ausgabe:
12.57
13.50
25
Area(5) wählt die int-Version, weil sie exakt passt; Area(2.0) wählt die double-Version. Methoden können sich nicht nur im Rückgabetyp unterscheiden: Zwei Methoden Area(int), die int und double zurückgeben, sind Fehler CS0111, Type 'Program' already defines a member called 'Area' with the same parameter types, weil ein Aufruf wie Area(5) sie nicht unterscheiden könnte.
Ein Aufruf, den keine Überladung annimmt, scheitert ebenfalls beim Kompilieren: Area(1, 2, 3) ist CS1501, No overload for method 'Area' takes 3 arguments, und Area("big") ist CS1503, Argument 1: cannot convert from 'string' to 'double'.
params: eine variable Anzahl von Argumenten
Ein params-Array-Parameter nimmt beliebig viele Argumente an, auch keines. Der Compiler baut das Array für dich:
Ausgabe:
empty cart: 0 item(s), 0
one item: 1 item(s), 9.99
three items: 3 item(s), 19.75
from array: 2 item(s), 50
Regeln: params muss der letzte Parameter sein (sonst CS0231), eine Methode hat höchstens einen, und ein übergebenes vorhandenes Array wird unverändert weitergereicht. Console.WriteLine("{0} and {1}", a, b) und string.Format sind Methoden mit params object[]. C# 13 hat params auf andere Collection-Typen wie List<T> und ReadOnlySpan<T> erweitert; davor waren nur Arrays erlaubt.
Statische Methoden und Instanzmethoden
Eine static-Methode gehört zum Typ und wird über den Typnamen aufgerufen: Math.Max(3, 7), Program.Total(...). Eine Instanzmethode gehört zu einem Objekt und kann dessen Felder verwenden:
Ausgabe:
12
Eine Instanzmethode aus einer statischen Methode ohne Objekt aufzurufen ist der häufigste Anfängerfehler in C#:
class Program
{
void Greet() => Console.WriteLine("hi");
static void Main()
{
Greet();
// error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
}
}
Markiere Greet als static oder erzeuge ein Objekt und rufe sie darauf auf: new Program().Greet();. Die Seite zu static behandelt statische Member und statische Klassen ausführlicher.
Die Main-Methode
In Main beginnt ein Programm. Sie muss static sein, und diese Signaturen sind gültig:
static void Main() { }
static void Main(string[] args) { } // command-line arguments
static int Main(string[] args) { return 0; } // exit code for the shell
static async Task Main() { } // C# 7.1 and later
Mit der Form async Task Main kann der Einstiegspunkt direkt await verwenden (siehe async und await). Seit C# 9 kann eine Datei stattdessen Top-Level-Anweisungen enthalten: Code ganz ohne Klasse oder Main, wie in einem neuen Projekt aus dotnet new console. Der Compiler erzeugt die Klasse und die Methode Main darum herum, es ist also dasselbe, nur kürzer geschrieben; args steht weiterhin zur Verfügung.
Rekursion
Eine Methode kann sich selbst aufrufen. Jeder Aufruf bekommt eigene Parameter und lokale Variablen, und ein Basisfall muss die Kette beenden:
Ausgabe:
120
2432902008176640000
35
Ohne Basisfall oder bei zu tiefer Eingabe fügt jeder Aufruf einen Stackframe hinzu, bis das Programm mit StackOverflowException stirbt. Diese Exception lässt sich nicht abfangen, schreibe die Rekursion bei Eingaben unbekannter Tiefe also als Schleife um.
Lokale Funktionen
C# 7.0 hat lokale Funktionen eingeführt: Methoden, die in einer anderen Methode deklariert und nur dort sichtbar sind. Sie können die Variablen der umgebenden Methode verwenden:
// C# 7.0 and later
static int CountValid(string[] codes)
{
int count = 0;
foreach (string code in codes)
{
if (IsValid(code)) count++;
}
return count;
bool IsValid(string c) => c.Length == 6 && char.IsLetter(c[0]);
}
Sie halten eine Hilfsmethode neben ihrem einzigen Aufrufer, ohne sie dem Rest der Klasse offenzulegen. Seit C# 8 können sie als static markiert werden, um das Erfassen äußerer Variablen zu verbieten. Wo eine lokale Funktion nicht verfügbar ist, erledigt eine private statische Methode in derselben Klasse dieselbe Aufgabe, wie in den anderen Beispielen auf dieser Seite.
Häufig gestellte Fragen
Was ist der Unterschied zwischen einer Methode und einer Funktion in C#?
In C# wird jede Funktion in einer Klasse, einem Struct oder einem Record deklariert und heißt Methode. Freistehende Funktionen gibt es nicht. Selbst Top-Level-Anweisungen (C# 9) und lokale Funktionen (C# 7) werden zu Methoden eines generierten oder umgebenden Typs kompiliert. „Funktion“ wird umgangssprachlich verwendet, und für Lambdas und Delegates.
Was macht params in C#?
Mit params nimmt eine Methode eine variable Anzahl von Argumenten eines Typs an: static int Sum(params int[] numbers) kann als Sum(), Sum(4), Sum(4, 5, 6) oder Sum(existingArray) aufgerufen werden. Der Compiler packt die Argumente in ein Array. Der params-Parameter muss der letzte sein, und eine Methode kann nur einen haben.
Was ist die Main-Methode in C#?
Main ist der Einstiegspunkt des Programms: die Methode, die die Runtime zuerst aufruft. Sie muss static sein, kann void oder int (den Exit-Code) zurückgeben und string[] args für die Kommandozeilenargumente annehmen. Seit C# 7.1 kann sie auch async Task Main sein, und seit C# 9 lassen Top-Level-Anweisungen den Compiler sie für dich erzeugen.
Wie gebe ich in C# mehrere Werte aus einer Methode zurück?
Gib ein Tupel zurück: static (int Min, int Max) Range(int[] values) und return (min, max);. Der Aufrufer kann result.Min lesen oder mit var (lo, hi) = Range(values); dekonstruieren. Für Werte, die in deinem Code gemeinsam unterwegs sind, ist eine kleine Klasse oder ein Record klarer; out-Parameter sind die ältere Alternative.
Warum bekomme ich „An object reference is required for the non-static field, method, or property“?
Eine static-Methode wie Main hat direkt eine Instanzmethode aufgerufen. Statische Methoden gehören zum Typ und haben kein Objekt, auf dem sie sie aufrufen könnten. Markiere entweder die aufgerufene Methode als static oder erzeuge zuerst ein Objekt: var app = new Program(); app.Greet();.
Was bedeutet „not all code paths return a value“?
Fehler CS0161: Eine Methode mit Rückgabetyp hat mindestens einen Pfad, der die schließende Klammer ohne return erreicht. Meist gibt ein if einen Wert zurück, aber danach fehlt ein else oder ein abschließendes return. Füge am Ende der Methode ein return (oder throw) hinzu.