Thực hiện các phương pháp và tham số lớp

Hoàn thành

Phương thức là một khối mã chứa một chuỗi câu lệnh. Một chương trình khiến các câu lệnh được thực thi bằng cách gọi phương thức và xác định bất kỳ đối số phương pháp bắt buộc nào. Trong C#, mỗi hướng dẫn thực hiện được thực hiện trong ngữ cảnh của một phương pháp.

Chữ ký phương thức

Các phương pháp được khai báo trong lớp, bản ghi hoặc hướng dẫn bằng cách xác định:

  • Mức truy nhập tùy chọn, chẳng hạn như public hoặc private. Mặc định là private.
  • Các bổ trợ tùy chọn như abstract hoặc sealed.
  • Giá trị trả về hoặc void phương thức không có.
  • Tên phương thức.
  • Bất kỳ tham số phương pháp nào. Tham số phương pháp được đặt trong dấu ngoặc đơn và được phân tách bởi dấu phẩy. Dấu ngoặc đơn trống chỉ ra rằng phương pháp không yêu cầu tham số.

Các phần này hoạt động cùng nhau để tạo chữ ký phương thức.

Quan trọng

Một loại trả lại của một phương pháp không phải là một phần của chữ ký của phương pháp cho các mục đích của quá tải phương pháp. Tuy nhiên, nó là một phần của chữ ký của phương pháp khi xác định tính tương thích giữa một đại diện và phương pháp mà nó trỏ tới.

Ví dụ sau đây xác định một lớp có tên Motorcycle có chứa năm phương pháp:


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();
    }
}

Lớp Motorcycle bao gồm một phương pháp quá tải, Drive. Hai phương pháp Drive có cùng tên nhưng có các kiểu tham số khác nhau.

Gọi phương thức

Các phương pháp có thể dụ hoặc tĩnh. Bạn phải tạo đối tượng để gọi một phương pháp thể hiện trên phiên bản đó; phương pháp phiên bản hoạt động trên phiên bản đó và dữ liệu của phiên bản đó. Bạn gọi ra một phương pháp tĩnh bằng cách tham chiếu tên của loại mà phương pháp thuộc về; phương pháp tĩnh không hoạt động trên dữ liệu phiên bản. Tìm cách gọi phương pháp tĩnh thông qua phiên bản đối tượng sẽ tạo ra lỗi trình biên dịch.

Gọi phương thức cũng giống như truy nhập một trường. Sau tên đối tượng (nếu bạn đang gọi phương thức phiên bản) hoặc tên kiểu (nếu bạn đang gọi phương thức tĩnh), hãy thêm dấu chấm, tên phương thức và dấu ngoặc đơn. Các đối số được liệt kê trong dấu ngoặc đơn và được phân tách bởi dấu phẩy.

Định nghĩa phương pháp xác định tên và kiểu của bất kỳ tham số nào được yêu cầu. Khi người gọi gọi ra phương thức, nó cung cấp các giá trị cụ thể, được gọi là đối số cho từng tham số. Các đối số phải tương thích với kiểu tham số, nhưng tên đối số, nếu một đối số được dùng trong mã gọi, không nhất thiết phải giống với tham số có tên được xác định trong phương pháp. Trong ví dụ sau đây, phương pháp Square gồm một tham số duy nhất thuộc kiểu int tên i. Các cuộc gọi phương pháp đầu tiên vượt qua Square phương pháp một biến của loại int tên là num; hằng số thứ hai; và thứ ba, một biểu thức.


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;
    }
}

Tham số tham chiếu và giá trị

Các kiểu trong C# là kiểu giá trị hoặc kiểu tham chiếu. Theo mặc định, cả kiểu giá trị và kiểu tham chiếu đều được chuyển theo giá trị cho một phương pháp.

Tham số loại giá trị

Khi một loại giá trị được chuyển đến một phương pháp theo giá trị, một bản sao của đối tượng thay vì đối tượng chính nó được truyền vào phương pháp. Do đó, thay đổi đối tượng trong phương pháp được gọi không có tác dụng trên các đối tượng ban đầu khi điều khiển trở về người gọi.

Ví dụ sau đây truyền một loại giá trị đến một phương pháp theo giá trị và phương pháp được gọi sẽ tìm cách thay đổi giá trị của kiểu giá trị. Nó xác định một biến loại int, là một loại giá trị, khởi tạo giá trị của nó thành 20 và chuyển nó đến một phương pháp có tên ModifyValue mà thay đổi giá trị của biến thành 30. Tuy nhiên, khi phương thức trả về, giá trị của biến số vẫn không thay đổi.


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

Khi một đối tượng có kiểu tham chiếu được chuyển tới một phương pháp theo giá trị, một tham chiếu đến đối tượng sẽ được chuyển qua theo giá trị. Nghĩa là, phương pháp không nhận được đối tượng, mà là một đối số cho biết vị trí của đối tượng. Nếu bạn thay đổi một thành viên của đối tượng bằng cách sử dụng tham chiếu này, sự thay đổi được phản ánh trong các đối tượng khi điều khiển trở về phương pháp gọi điện thoại. Tuy nhiên, việc thay thế đối tượng được chuyển sang phương pháp không có tác dụng trên đối tượng ban đầu khi điều khiển trở về người gọi.

Ví dụ sau đây xác định một lớp (là loại tham chiếu) có tên là SampleRefType. Nó tạo đối tượng SampleRefType, gán 44 cho trường value của nó và truyền đối tượng đến phương pháp ModifyObject của nó. Ví dụ này về cơ bản không giống như ví dụ trước (nó truyền một đối số theo giá trị đến một phương pháp). Tuy nhiên, kết quả khác nhau vì kiểu tham chiếu được sử dụng chứ không phải loại giá trị. Sửa đổi được thực hiện trong ModifyObject trường obj.value cũng thay đổi trường value của đối số, rt. Khi phương pháp Main hiển thị giá trị của rt chúng ta thấy rằng nó đã được cập nhật thành 33, như kết quả từ ví dụ hiển thị.


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

Tham số kiểu tham chiếu

Bạn truyền tham số theo tham chiếu khi bạn muốn thay đổi giá trị của đối số trong phương pháp và muốn phản ánh thay đổi đó khi điều khiển quay lại phương thức gọi. Để truyền tham số theo tham chiếu, bạn sử dụng từ khóa ref hoặc out tham số. Bạn cũng có thể chuyển một giá trị theo tham chiếu để tránh sao chép nhưng vẫn ngăn sửa đổi bằng cách sử in từ khóa.

Ví dụ sau đây giống hệt giá trị trước đó, ngoại trừ giá trị được chuyển qua bằng tham chiếu đến phương ModifyValue trước. Khi giá trị của tham số được thay đổi trong phương pháp ModifyValue, thay đổi trong giá trị được phản ánh khi điều khiển trở về người gọi.


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

Một mẫu hình phổ biến sử dụng tham số tham số tham chiếu liên quan đến việc hoán đổi các giá trị của biến số. Bạn vượt qua hai biến để một phương pháp bằng tham chiếu, và phương pháp trao đổi nội dung của họ. Ví dụ sau đây hoán đổi các giá trị số nguyên.


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

Việc truyền tham số kiểu tham chiếu cho phép bạn thay đổi giá trị của chính tham chiếu đó, chứ không phải là giá trị của các thành phần hoặc trường riêng lẻ của nó.

Bộ sưu tập tham số

Đôi khi, yêu cầu bạn chỉ định số đối số chính xác cho phương pháp của bạn là hạn chế. Bằng cách sử dụng từ params để cho biết tham số là một tuyển tập tham số, bạn cho phép gọi phương thức của mình với số lượng tham đối biến đổi. Tham số được gắn thẻ với từ khóa params phải là kiểu tuyển tập và nó phải là tham số cuối cùng trong danh sách tham số của phương thức.

Sau đó, người gọi có thể gọi phương thức theo một trong bốn cách cho tham số params:

  • Bằng cách truyền tuyển tập các kiểu thích hợp có chứa số lượng thành phần mong muốn. Ví dụ này sử dụng biểu thức bộ sưu tập để trình biên dịch tạo loại tuyển tập thích hợp.
  • Bằng cách truyền danh sách các đối số riêng lẻ được phân tách bằng dấu phẩy của kiểu thích hợp cho phương pháp. Trình biên dịch tạo loại bộ sưu tập thích hợp.
  • Bằng cách đi null.
  • Bằng cách không cung cấp tham đối cho bộ sưu tập tham số.

Ví dụ sau đây xác định một phương pháp có tên GetVowels trả về tất cả nguyên âm từ một tuyển tập tham số. Phương Main minh họa tất cả bốn cách kích hoạt phương pháp. Người gọi không bắt buộc phải cung cấp bất kỳ đối số nào cho các tham số bao gồm params đổi số. Trong trường hợp đó, tham số là một tuyển tập trống.


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: ''

Ghi

Trước C# 13, bổ params chỉ có thể được dùng với một mảng chiều duy nhất.

Trả về giá trị của phương thức

Các phương pháp có thể trả về giá trị cho người gọi. Nếu kiểu trả về (loại được liệt kê trước tên phương thức) không phải là void, phương pháp có thể trả về giá trị bằng cách sử dụng từ return của bạn. Câu lệnh với từ khóa return tiếp theo là một biến số, hằng số hoặc biểu thức khớp với kiểu trả về sẽ trả về giá trị đó cho người gọi phương thức. Các phương pháp có kiểu trả về không phải làvoid được yêu cầu để sử dụng return khóa để trả về một giá trị. Từ return cũng dừng việc thực thi phương pháp.

Nếu kiểu trả về không voidthì câu lệnh trả về không có giá trị vẫn hữu ích để dừng thực thi phương pháp. Nếu không có return từ khóa, phương pháp sẽ ngừng thực thi khi đến cuối khối mã.

Ví dụ: hai phương pháp này sử dụng từ return để trả về số nguyên:


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

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

Ví dụ này sử dụng các phần tử biểu thức cơ thể để gán giá trị trả về của các phương pháp. Cú pháp này là viết tắt cho các phương pháp sử dụng một câu lệnh (biểu thức) để tính toán giá trị trả về.

Bạn cũng có thể chọn xác định phương pháp của mình với nội dung câu lệnh và một return sau:


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

Để sử dụng một giá trị được trả về từ một phương thức, phương pháp gọi có thể dùng phương pháp tự gọi chính nó bất cứ nơi nào một giá trị cùng loại sẽ là đủ. Bạn cũng có thể gán giá trị trả về cho một biến số. Ví dụ: ba ví dụ về mã sau đây thực hiện cùng một mục tiêu:


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);

Đôi khi, bạn muốn phương pháp của mình trả về nhiều hơn một giá trị duy nhất. Bạn sử các loại bộký tự bộ để trả về nhiều giá trị. Kiểu bộ xác định kiểu dữ liệu của các thành phần của bộ. Ký tự Tuple cung cấp giá trị thực tế của bộ được trả về. Trong ví dụ sau đây, (string, string, string, int) xác định loại bộ do phương pháp GetPersonalInfo trả về. Biểu thức (per.FirstName, per.MiddleName, per.LastName, per.Age) là chữ bộ; phương pháp này trả về tên, tên đệm và họ, cùng với tuổi, của một đối PersonInfo gia đình.


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

Khi đó người gọi có thể sử dụng bộ trả về bằng cách sử dụng mã sau:


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

Tên cũng có thể được gán cho các thành phần bộ trong định nghĩa kiểu bộ. Ví dụ sau đây cho thấy một phiên bản thay thế của phương pháp GetPersonalInfo sử dụng các phần tử đã đặt tên:


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);
}

Sau đó, có thể sửa đổi cuộc gọi trước đó đến phương pháp GetPersonalInfo như sau:


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

Nếu phương pháp nhận một mảng làm tham số và sửa đổi giá trị của các phần tử riêng lẻ, thì phương pháp trả về mảng là không cần thiết. C# truyền tất cả các kiểu tham chiếu theo giá trị và giá trị của tham chiếu mảng là con trỏ trỏ tới mảng.

Các thành viên cơ thể biểu thức

Thông thường, có các định nghĩa phương pháp trả về ngay lập tức với kết quả của một biểu thức hoặc có một câu lệnh duy nhất làm nội dung của phương thức. Có một lối tắt cú pháp để xác định các phương pháp như vậy bằng cách =>:


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);

Nếu phương thức trả về void phương pháp không đồng bộ thì nội dung của phương pháp phải là một biểu thức câu lệnh (tương tự như với lambdas). Đối với thuộc tính và bộ lập chỉ mục, chúng phải ở dạng chỉ đọc và bạn không sử dụng từ get truy nhập mới.