Metoda to nazwany blok kodu, który przyjmuje dane wejściowe (parametry), wykonuje swoją pracę i może oddać wynik (wartość zwracaną). W C# każda metoda żyje wewnątrz typu; nie ma wolnostojących funkcji.
Wynik:
Headphones 48.00
Cable 11.40
Budowa metody
public static decimal PriceWithTax(decimal net, decimal taxRate)
{
return net * (1 + taxRate);
}
- Modyfikator dostępu (
public,private, ...): kto może ją wywołać. Bez niego metoda jestprivatew swoim typie. static: metoda należy do typu, a nie do obiektu. Więcej o tym poniżej.- Typ zwracany: typ zwracanej wartości albo
void, gdy metoda nic nie zwraca. - Nazwa: zwyczajowo w PascalCase, zwykle czasownik (
Calculate,SendEmail,TryParse). - Lista parametrów: zero lub więcej par
type name. Każdy parametr to zmienna lokalna zainicjalizowana argumentem od wywołującego. - Ciało: instrukcje między nawiasami klamrowymi.
Nazwa wraz z typami parametrów (i ewentualnymi modyfikatorami ref, out lub in) tworzy sygnaturę metody. Typ zwracany do niej nie należy, co ma znaczenie przy przeciążaniu.
Wartości zwracane i void
return natychmiast kończy metodę i oddaje wartość. Metoda z typem zwracanym musi zwrócić wartość tego typu na każdej ścieżce wykonania:
static string Grade(int score)
{
if (score >= 50)
return "pass";
} // error CS0161: 'Program.Grade(int)': not all code paths return a value
Kompilator sprawdza każdą gałąź. Dodaj brakujące return "fail"; albo throw, jeśli dotarcie do tego miejsca oznacza błąd.
Metoda void niczego nie zwraca. Nadal może jednak użyć samego return;, żeby zakończyć się wcześniej:
Wynik:
ana@mail.com: newsletter sent
ben@mail.com: not subscribed, skipped
False
True
Metody z ciałem wyrażeniowym
Gdy ciało metody to jedno wyrażenie, => zastępuje nawiasy klamrowe i 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}");
To ta sama metoda, tylko zapisana krócej. Działa to też dla metod void, o ile ciało jest jednym wyrażeniem (wywołaniem, przypisaniem).
Parametry to kopie
Argumenty są domyślnie przekazywane przez wartość: parametr dostaje kopię argumentu. Przypisanie do parametru wewnątrz metody nie wpływa na zmienną wywołującego:
Wynik:
inside: 42
after: 21
scores: 70, 85, 100
List się zmieniła, bo dla typu referencyjnego kopiowaną wartością jest referencja: metoda i wywołujący wskazują ten sam obiekt listy. Przypisanie scores = new List<int>() wewnątrz metody nie wpłynęłoby na wywołującego. Żeby metoda mogła przypisać wartość zmiennej wywołującego, użyj ref albo out; żeby nadać parametrom wartości domyślne i przekazywać je po nazwie, użyj parametrów opcjonalnych i argumentów nazwanych.
Zwracanie kilku wartości w krotce
Od C# 7.0 metoda może zwrócić krotkę z nazwanymi elementami, a wywołujący może ją rozłożyć na osobne zmienne:
Wynik:
min 15, max 27, avg 21.0
range 12
_ odrzuca element, którego nie potrzebujesz. Dla wyników używanych w wielu miejscach mała klasa lub rekord z nazwanymi właściwościami lepiej opisuje ich kształt niż krotka.
Przeciążanie
Kilka metod może mieć tę samą nazwę, jeśli ich listy parametrów różnią się liczbą, typami lub kolejnością typów. Kompilator wybiera najlepsze dopasowanie dla każdego wywołania:
Wynik:
12.57
13.50
25
Area(5) wybiera wersję dla int, bo to dokładne dopasowanie; Area(2.0) wybiera wersję dla double. Metody nie mogą różnić się tylko typem zwracanym: dwie metody Area(int) zwracające int i double to błąd CS0111, Type 'Program' already defines a member called 'Area' with the same parameter types, bo wywołanie takie jak Area(5) nie pozwoliłoby ich rozróżnić.
Wywołanie, którego nie przyjmuje żadne przeciążenie, też kończy się błędem kompilacji: Area(1, 2, 3) to CS1501, No overload for method 'Area' takes 3 arguments, a Area("big") to CS1503, Argument 1: cannot convert from 'string' to 'double'.
params: zmienna liczba argumentów
Parametr będący tablicą params przyjmuje dowolną liczbę argumentów, także zero. Kompilator buduje tablicę za ciebie:
Wynik:
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
Zasady: params musi być ostatnim parametrem (w przeciwnym razie CS0231), metoda ma co najwyżej jeden taki parametr, a przekazanie istniejącej tablicy oddaje ją bez zmian. Console.WriteLine("{0} and {1}", a, b) i string.Format to metody z params object[]. C# 13 rozszerzył params na inne typy kolekcji, takie jak List<T> i ReadOnlySpan<T>; wcześniej dozwolone były tylko tablice.
Metody statyczne i instancyjne
Metoda static należy do typu i wywołuje się ją na nazwie typu: Math.Max(3, 7), Program.Total(...). Metoda instancyjna należy do obiektu i może używać jego pól:
Wynik:
12
Wywołanie metody instancyjnej z metody statycznej bez obiektu to najczęstszy błąd początkujących w 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()'
}
}
Oznacz Greet jako static albo utwórz obiekt i wywołaj metodę na nim: new Program().Greet();. Strona o static opisuje dokładniej składowe statyczne i klasy statyczne.
Metoda Main
Main to miejsce, w którym zaczyna się program. Musi być static, a poprawne są te sygnatury:
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
Forma async Task Main pozwala punktowi wejścia bezpośrednio używać await (zobacz async i await). Od C# 9 plik może zamiast tego zawierać instrukcje najwyższego poziomu: kod bez żadnej klasy ani Main, jak w nowym projekcie dotnet new console. Kompilator generuje wokół niego klasę i metodę Main, więc to ta sama rzecz zapisana krócej; args nadal jest dostępne.
Rekurencja
Metoda może wywołać samą siebie. Każde wywołanie dostaje własne parametry i zmienne lokalne, a przypadek bazowy musi przerwać łańcuch:
Wynik:
120
2432902008176640000
35
Bez przypadku bazowego albo przy zbyt głębokich danych każde wywołanie dokłada ramkę stosu, aż program padnie z StackOverflowException. Tego wyjątku nie da się przechwycić, więc dla danych o nieznanej głębokości przepisz rekurencję na pętlę.
Funkcje lokalne
C# 7.0 wprowadził funkcje lokalne: metody zadeklarowane wewnątrz innej metody i widoczne tylko tam. Mogą używać zmiennych metody otaczającej:
// 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]);
}
Trzymają funkcję pomocniczą obok jej jedynego wywołującego, nie wystawiając jej reszcie klasy. Od C# 8 można je oznaczyć jako static, by zabronić przechwytywania zmiennych zewnętrznych. Tam, gdzie funkcja lokalna nie jest dostępna, tę samą pracę wykona prywatna metoda statyczna w tej samej klasie, jak w pozostałych przykładach na tej stronie.
Najczęściej zadawane pytania
Jaka jest różnica między metodą a funkcją w C#?
W C# każda funkcja jest zadeklarowana wewnątrz klasy, struktury lub rekordu i nazywa się metodą. Nie ma wolnostojących funkcji. Nawet instrukcje najwyższego poziomu (C# 9) i funkcje lokalne (C# 7) są kompilowane do metod wygenerowanego lub otaczającego typu. Słowa "funkcja" używa się nieformalnie oraz w odniesieniu do lambd i delegatów.
Co robi params w C#?
params pozwala metodzie przyjąć zmienną liczbę argumentów jednego typu: static int Sum(params int[] numbers) można wywołać jako Sum(), Sum(4), Sum(4, 5, 6) albo Sum(existingArray). Kompilator pakuje argumenty do tablicy. Parametr params musi być ostatni, a metoda może mieć tylko jeden.
Czym jest metoda Main w C#?
Main to punkt wejścia programu: metoda, którą środowisko uruchomieniowe wywołuje jako pierwszą. Musi być static, może zwracać void albo int (kod wyjścia) i może przyjmować string[] args z argumentami wiersza poleceń. Od C# 7.1 może też mieć postać async Task Main, a od C# 9 instrukcje najwyższego poziomu pozwalają kompilatorowi wygenerować ją za ciebie.
Jak zwrócić kilka wartości z metody w C#?
Zwróć krotkę: static (int Min, int Max) Range(int[] values) i return (min, max);. Wywołujący może odczytać result.Min albo rozłożyć wynik przez var (lo, hi) = Range(values);. Dla wartości, które wędrują razem po całym kodzie, czytelniejsza jest mała klasa lub rekord; parametry out to starsza alternatywa.
Skąd błąd "An object reference is required for the non-static field, method, or property"?
Metoda static, taka jak Main, wywołała bezpośrednio metodę instancyjną. Metody statyczne należą do typu i nie mają obiektu, na którym mogłyby ją wywołać. Albo oznacz wywoływaną metodę jako static, albo najpierw utwórz obiekt: var app = new Program(); app.Greet();.
Co oznacza "not all code paths return a value"?
Błąd CS0161: metoda z typem zwracanym ma co najmniej jedną ścieżkę, która dochodzi do zamykającego nawiasu klamrowego bez return. Zwykle if zwraca wartość, ale po nim nie ma else ani końcowego return. Dodaj return (albo throw) na końcu metody.