Implementowanie metod i parametrów klas

Zakończone

Metoda to blok kodu, który zawiera serię instrukcji. Program powoduje wykonanie instrukcji przez wywołanie metody i określenie wszelkich wymaganych argumentów metody. W języku C# każda wykonywana instrukcja jest wykonywana w kontekście metody.

Podpisy metod

Metody są deklarowane w klasie, rekordzie lub w strukturę, określając:

  • Opcjonalny poziom dostępu, taki jak public lub private. Wartość domyślna to private.
  • Opcjonalne modyfikatory, takie jak abstract lub sealed.
  • Wartość zwracana lub void, jeśli metoda nie ma żadnej.
  • Nazwa metody.
  • Wszystkie parametry metody. Parametry metody są ujęte w nawiasy i są oddzielone przecinkami. Puste nawiasy wskazują, że metoda nie wymaga żadnych parametrów.

Te części współpracują ze sobą w celu utworzenia podpisu metody.

Ważny

Zwracany typ metody nie jest częścią podpisu metody na potrzeby przeciążenia metody. Jednak jest to część podpisu metody podczas określania zgodności między delegatem a metodą, do której wskazuje.

W poniższym przykładzie zdefiniowano klasę o nazwie Motorcycle zawierającą pięć metod:


namespace MotorCycleExample
{
    abstract class Motorcycle
    {
        // Anyone can call this.
        public void StartEngine() {/* Method statements here */ }

        // Only derived classes can call this.
        protected void AddGas(int gallons) { /* Method statements here */ }

        // Derived classes can override the base class implementation.
        public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; }

        // Derived classes can override the base class implementation.
        public virtual int Drive(TimeSpan time, int speed) { /* Method statements here */ return 0; }

        // Derived classes must implement this.
        public abstract double GetTopSpeed();
    }
}

Klasa Motorcycle zawiera przeciążone metody Drive. Dwie metody Drive mają taką samą nazwę, ale różne typy parametrów.

Wywołanie metody

Metody mogą być instancją lub statyczne. Należy utworzyć wystąpienie obiektu, aby wywołać metodę wystąpienia w tym wystąpieniu; metoda wystąpienia działa na tym wystąpieniu i jego danych. Metoda statyczna jest wywoływana przez odwoływanie się do nazwy typu, do którego należy metoda; metody statyczne nie działają na danych wystąpienia. Próba wywołania metody statycznej za pomocą wystąpienia obiektu generuje błąd kompilatora.

Wywoływanie metody przypomina uzyskiwanie dostępu do pola. Po nazwie obiektu (jeśli wywołujesz metodę wystąpienia) lub nazwie typu (jeśli wywołujesz metodę statyczną), dodaj kropkę, nazwę metody i nawiasy. Argumenty są wymienione w nawiasach i są rozdzielane przecinkami.

Definicja metody określa nazwy i typy wszystkich wymaganych parametrów. Gdy obiekt wywołujący wywołuje metodę, udostępnia konkretne wartości, nazywane argumentami, dla każdego parametru. Argumenty muszą być zgodne z typem parametru, ale nazwa argumentu, jeśli jest używana w kodzie wywołującym, nie musi być taka sama jak parametr o nazwie zdefiniowanej w metodzie. W poniższym przykładzie metoda Square zawiera jeden parametr typu int o nazwie i. Pierwsze wywołanie metody przekazuje metodę Square zmienną typu int o nazwie num; druga, stała liczbowa; i trzeci, wyrażenie.


public static class SquareExample
{
    public static void Main()
    {
        // Call with an int variable.
        int num = 4;
        int productA = Square(num);

        // Call with an integer literal.
        int productB = Square(12);

        // Call with an expression that evaluates to int.
        int productC = Square(productA * 3);
    }

    static int Square(int i)
    {
        // Store input argument in a local variable.
        int input = i;
        return input * input;
    }
}

Parametry wartości i odwołania

Typy w języku C# to typy wartości lub typy referencyjne. Domyślnie zarówno typy wartości, jak i typy referencyjne są przekazywane przez wartość do metody.

Parametry typu wartości

Gdy typ wartości jest przekazywany do metody według wartości, kopia obiektu zamiast samego obiektu jest przekazywana do metody . W związku z tym zmiany obiektu w wywoływanej metodzie nie mają wpływu na oryginalny obiekt, gdy kontrolka powraca do obiektu wywołującego.

Poniższy przykład przekazuje typ wartości do metody według wartości, a wywołana metoda próbuje zmienić wartość typu wartości. Definiuje zmienną typu int, która jest typem wartości, inicjuje jego wartość do 20 i przekazuje ją do metody o nazwie ModifyValue, która zmienia wartość zmiennej na 30. Gdy jednak metoda zwróci wartość zmiennej, pozostaje niezmieniona.


public static class ByValueExample
{
    public static void Main()
    {
        var value = 20;
        Console.WriteLine("In Main, value = {0}", value);
        ModifyValue(value);
        Console.WriteLine("Back in Main, value = {0}", value);
    }

    static void ModifyValue(int i)
    {
        i = 30;
        Console.WriteLine("In ModifyValue, parameter value = {0}", i);
        return;
    }
}
// The example displays the following output:
//      In Main, value = 20
//      In ModifyValue, parameter value = 30
//      Back in Main, value = 20

Gdy obiekt typu odwołania jest przekazywany do metody według wartości, odwołanie do obiektu jest przekazywane przez wartość. Oznacza to, że metoda nie odbiera samego obiektu, ale argument wskazujący lokalizację obiektu. Jeśli zmienisz element członkowski obiektu przy użyciu tego odwołania, zmiana zostanie odzwierciedlona w obiekcie, gdy kontrolka powróci do metody wywołującej. Jednak zastąpienie obiektu przekazanego do metody nie ma wpływu na oryginalny obiekt, gdy kontrolka powróci do obiektu wywołującego.

W poniższym przykładzie zdefiniowano klasę (która jest typem odwołania) o nazwie SampleRefType. Tworzy wystąpienie obiektu SampleRefType, przypisuje 44 do pola value i przekazuje obiekt do metody ModifyObject. W tym przykładzie zasadniczo jest to samo, co w poprzednim przykładzie (przekazuje argument według wartości do metody). Jednak wynik jest inny, ponieważ używany jest typ odwołania, a nie typ wartości. Modyfikacja wprowadzona w ModifyObject w polu obj.value również zmienia pole value argumentu, rt. Gdy metoda Main wyświetla wartość rt zobaczymy, że została zaktualizowana do 33, jak pokazano w danych wyjściowych z przykładu.


public class SampleRefType
{
    public int value;
}

public static class ByRefTypeExample
{
    public static void Main()
    {
        var rt = new SampleRefType { value = 44 };
        Console.WriteLine("In Main, rt.value = {0}", rt.value);
        ModifyObject(rt);
        Console.WriteLine("Back in Main, rt.value = {0}", rt.value);
    }

    static void ModifyObject(SampleRefType obj)
    {
        obj.value = 33;
        Console.WriteLine("In ModifyObject, obj.value = {0}", obj.value);
    }
}
// The example displays the following output:
//      In Main, rt.value = 44
//      In ModifyObject, obj.value = 33
//      Back in Main, rt.value = 33

Parametry typu odwołania

Parametr jest przekazywany przez odwołanie, gdy chcesz zmienić wartość argumentu w metodzie i chcesz odzwierciedlić tę zmianę, gdy kontrolka powróci do metody wywołującej. Aby przekazać parametr przy użyciu odwołania, należy użyć słowa kluczowego ref lub out. Możesz również przekazać wartość przez odwołanie, aby uniknąć kopiowania, ale nadal zapobiegać modyfikacjom przy użyciu słowa kluczowego in.

Poniższy przykład jest identyczny z poprzednim, z wyjątkiem wartości przekazywanej przez odwołanie do metody ModifyValue. Gdy wartość parametru zostanie zmodyfikowana w metodzie ModifyValue, zmiana wartości zostanie odzwierciedlona, gdy kontrolka powróci do obiektu wywołującego.


public static class ByRefExample
{
    public static void Main()
    {
        var value = 20;
        Console.WriteLine("In Main, value = {0}", value);
        ModifyValue(ref value);
        Console.WriteLine("Back in Main, value = {0}", value);
    }

    private static void ModifyValue(ref int i)
    {
        i = 30;
        Console.WriteLine("In ModifyValue, parameter value = {0}", i);
        return;
    }
}
// The example displays the following output:
//      In Main, value = 20
//      In ModifyValue, parameter value = 30
//      Back in Main, value = 30

Typowy wzorzec używany przez parametry ref obejmuje zamianę wartości zmiennych. Przekazujesz dwie zmienne do metody według odwołania, a metoda zamienia ich zawartość. Poniższy przykład zamienia wartości całkowite.


public static class RefSwapExample
{
    static void Main()
    {
        int i = 2, j = 3;
        Console.WriteLine("i = {0}  j = {1}", i, j);

        Swap(ref i, ref j);

        Console.WriteLine("i = {0}  j = {1}", i, j);
    }

    static void Swap(ref int x, ref int y) =>
        (y, x) = (x, y);
}
// The example displays the following output:
//      i = 2  j = 3
//      i = 3  j = 2

Przekazanie parametru typu odwołania umożliwia zmianę wartości samego odwołania, a nie wartości poszczególnych elementów lub pól.

Kolekcje parametrów

Czasami wymaganie, aby określić dokładną liczbę argumentów dla metody, jest restrykcyjne. Używając słowa kluczowego params, aby wskazać, że parametr jest kolekcją parametrów, można wywołać metodę ze zmienną liczbą argumentów. Parametr otagowany za pomocą słowa kluczowego params musi być typem kolekcji i musi być ostatnim parametrem na liście parametrów metody.

Obiekt wywołujący może następnie wywołać metodę na jeden z czterech sposobów dla parametru params:

  • Przekazując kolekcję odpowiedniego typu zawierającego żądaną liczbę elementów. W przykładzie użyto wyrażenia kolekcji, aby kompilator tworzy odpowiedni typ kolekcji.
  • Przekazując rozdzielaną przecinkami listę poszczególnych argumentów odpowiedniego typu do metody. Kompilator tworzy odpowiedni typ kolekcji.
  • Przekazując null.
  • Nie podając argumentu do kolekcji parametrów.

W poniższym przykładzie zdefiniowano metodę o nazwie GetVowels, która zwraca wszystkie vowels z kolekcji parametrów. Metoda Main ilustruje wszystkie cztery sposoby wywoływania metody. Obiekty wywołujące nie są wymagane do podania żadnych argumentów dla parametrów, które obejmują modyfikator params. W takim przypadku parametr jest pustą kolekcją.


static class ParamsExample
{
    static void Main()
    {
        string fromArray = GetVowels(["apple", "banana", "pear"]);
        Console.WriteLine($"Vowels from collection expression: '{fromArray}'");

        string fromMultipleArguments = GetVowels("apple", "banana", "pear");
        Console.WriteLine($"Vowels from multiple arguments: '{fromMultipleArguments}'");

        string fromNull = GetVowels(null);
        Console.WriteLine($"Vowels from null: '{fromNull}'");

        string fromNoValue = GetVowels();
        Console.WriteLine($"Vowels from no value: '{fromNoValue}'");
    }

    static string GetVowels(params IEnumerable<string>? input)
    {
        if (input == null || !input.Any())
        {
            return string.Empty;
        }

        char[] vowels = ['A', 'E', 'I', 'O', 'U'];
        return string.Concat(
            input.SelectMany(
                word => word.Where(letter => vowels.Contains(char.ToUpper(letter)))));
    }
}

// The example displays the following output:
//     Vowels from array: 'aeaaaea'
//     Vowels from multiple arguments: 'aeaaaea'
//     Vowels from null: ''
//     Vowels from no value: ''

Nuta

Przed językiem C# 13 modyfikator params może być używany tylko z jednowymiarową tablicą.

Zwracane wartości metod

Metody mogą zwracać wartość do elementu wywołującego. Jeśli typ zwracany (typ wymieniony przed nazwą metody) nie jest void, metoda może zwrócić wartość przy użyciu słowa kluczowego return. Instrukcja ze słowem kluczowym return, po którym następuje zmienna, stała lub wyrażenie zgodne z typem zwracanym, zwraca tę wartość do wywołującego metodę. Metody z typem zwrotnym niezwiązanym są wymagane do użycia słowa kluczowego return w celu zwrócenia wartości. Słowo kluczowe return również zatrzymuje wykonywanie metody .

Jeśli zwracany typ jest void, instrukcja return bez wartości jest nadal przydatna do zatrzymania wykonywania metody. Bez słowa kluczowego return metoda zatrzymuje wykonywanie po osiągnięciu końca bloku kodu.

Na przykład te dwie metody używają słowa kluczowego return, aby zwrócić liczby całkowite:


class SimpleMath
{
    public int AddTwoNumbers(int number1, int number2) =>
        number1 + number2;

    public int SquareANumber(int number) =>
        number * number;
}

W tym przykładzie użyto składowych wyrażeń w celu przypisania wartości zwracanej metod. Ta składnia jest skrótem metod, które używają pojedynczej instrukcji (wyrażenia) do obliczenia wartości zwracanej.

Możesz również zdefiniować metody za pomocą treści instrukcji i instrukcji return:


class SimpleMathExtnsion
{
    public int DivideTwoNumbers(int number1, int number2)
    {
        return number1 / number2;
    }
}

Aby użyć wartości zwróconej z metody, metoda wywołująca może użyć wywołania metody w dowolnym miejscu, w jakim wartość tego samego typu byłaby wystarczająca. Możesz również przypisać wartość zwracaną do zmiennej. Na przykład następujące trzy przykłady kodu osiągają ten sam cel:


int result = obj.AddTwoNumbers(1, 2);
result = obj.SquareANumber(result);
// The result is 9.
Console.WriteLine(result);


result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);


result = obj2.DivideTwoNumbers(6,2);
// The result is 3.
Console.WriteLine(result);

Czasami chcesz, aby metoda zwróciła więcej niż jedną wartość. Aby zwrócić wiele wartości, należy użyć typów krotki i literałów krotki . Typ krotki definiuje typy danych elementów krotki. Literały krotki zapewniają rzeczywiste wartości zwracanej krotki. W poniższym przykładzie (string, string, string, int) definiuje typ krotki zwracany przez metodę GetPersonalInfo. Wyrażenie (per.FirstName, per.MiddleName, per.LastName, per.Age) jest literałem krotki; metoda zwraca pierwszą, środkową i rodzinną nazwę wraz z wiekiem obiektu PersonInfo.


public (string, string, string, int) GetPersonalInfo(string id)
{
    PersonInfo per = PersonInfo.RetrieveInfoById(id);
    return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}

Obiekt wywołujący może następnie korzystać z zwróconej krotki przy użyciu następującego kodu:


var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.Item1} {person.Item3}: age = {person.Item4}");

Nazwy można również przypisać do elementów krotki w definicji typu krotki. W poniższym przykładzie przedstawiono alternatywną wersję metody GetPersonalInfo, która używa nazwanych elementów:


public (string FName, string MName, string LName, int Age) GetPersonalInfo(string id)
{
    PersonInfo per = PersonInfo.RetrieveInfoById(id);
    return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}

Poprzednie wywołanie metody GetPersonalInfo można zmodyfikować w następujący sposób:


var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.FName} {person.LName}: age = {person.Age}");

Jeśli metoda przyjmuje tablicę jako parametr i modyfikuje wartość poszczególnych elementów, nie jest konieczne, aby metoda zwróciła tablicę. Język C# przekazuje wszystkie typy odwołań według wartości, a wartość odwołania do tablicy jest wskaźnikiem do tablicy.

Składowe wyrażeń

Często zdarza się, że definicje metod zwracają się natychmiast z wynikiem wyrażenia lub mają pojedynczą instrukcję jako treść metody. Istnieje skrót składniowy do definiowania takich metod przy użyciu =>:


public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);

Jeśli metoda zwraca void lub jest metodą asynchroniową, treść metody musi być wyrażeniem instrukcji (takim samym jak w przypadku lambd). W przypadku właściwości i indeksatorów muszą być tylko do odczytu i nie należy używać słowa kluczowego get dostępu.