Khi bạn chuyển một đối số bằng tham chiếu, bạn chuyển một con trỏ tới giá trị trong bộ nhớ. Hàm hoạt động trên đối số. Khi một hàm thay đổi giá trị của một đối số được truyền bởi tham chiếu, giá trị ban đầu sẽ thay đổi
Khi bạn chuyển một đối số theo giá trị, bạn chuyển một bản sao của giá trị đó vào bộ nhớ. Chức năng hoạt động trên bản sao. Điều này có nghĩa là khi một hàm thay đổi giá trị của một đối số được truyền theo giá trị, hiệu ứng sẽ là cục bộ đối với hàm đó;
Việc một đối số được truyền theo tham chiếu hay theo giá trị tùy thuộc vào kiểu dữ liệu và các đặc điểm khác của đối số
- Mảng, danh sách, thể hiện kiểu và đối tượng phải được chuyển qua tham chiếu
Ghi chú. Tham số mảng không được khai báo là "ByVal"; .
- Các hằng số và biểu thức được tự động truyền theo giá trị
- Các đối số khác có thể được chuyển theo một trong hai cách, như được chỉ định trong định nghĩa hoặc lệnh gọi. Các đối số cho hàm và phụ được truyền theo tham chiếu trừ khi định nghĩa hoặc lệnh gọi chỉ định truyền theo giá trị
Chuyển qua tham chiếu
Biến phải có cùng kiểu dữ liệu với tham số tương ứng trong định nghĩa hàm, trừ khi tham số được khai báo là Variant hoặc là biến đối tượng. Một biến đối tượng có thể được truyền cho một đối tượng của cùng một lớp cơ sở hoặc lớp dẫn xuất. Sau này, lớp cơ sở phải chứa một thể hiện của lớp dẫn xuất hoặc một lớp dẫn xuất từ lớp dẫn xuất
Nếu sau đó biến được sửa đổi bởi hàm hoặc phụ, thì biến đó có giá trị được sửa đổi khi hàm hoặc phụ trả về
Truyền theo giá trị
Bạn có thể làm như sau
- Sử dụng từ khóa ByVal trong khai báo của đối số trong hàm hoặc định nghĩa phụ
Đối số được truyền theo giá trị bất cứ khi nào hàm hoặc phụ được gọi
- Chèn dấu ngoặc đơn xung quanh đối số trong hàm hoặc lệnh gọi phụ
Bạn có thể kiểm soát xem một đối số được truyền theo tham chiếu hay theo giá trị tại thời điểm hàm hoặc phụ được gọi
Một giá trị được chuyển đến một hàm hoặc đối số phụ sẽ tự động được chuyển đổi thành kiểu dữ liệu của hàm hoặc đối số phụ nếu có thể chuyển đổi. Đối số Biến thể sẽ chấp nhận giá trị của bất kỳ loại dữ liệu dựng sẵn nào; . Đối số Biến thể sẽ không chấp nhận giá trị của loại do người dùng xác định. Tuy nhiên, hãy nhớ rằng danh sách, mảng, đối tượng và kiểu do người dùng xác định không thể và do đó không nên được chuyển theo giá trị
Nếu đối số biến sau đó được sửa đổi bởi hàm hoặc phụ, thì biến có giá trị ban đầu sau khi hàm hoặc phụ trả về. Hàm hoặc phụ chỉ hoạt động trên bản sao đã truyền của biến, vì vậy bản thân biến đó không thay đổi
Hai cách truyền đối số cho phương thức trong nhiều ngôn ngữ lập trình là truyền theo giá trị và truyền theo tham chiếu. Khi một đối số được truyền theo giá trị [mặc định trong C#], một bản sao giá trị của nó được tạo và truyền cho phương thức được gọi. Các thay đổi đối với bản sao không ảnh hưởng đến giá trị của biến ban đầu trong trình gọi. Điều này ngăn ngừa các tác dụng phụ ngẫu nhiên cản trở rất nhiều đến sự phát triển của các hệ thống phần mềm chính xác và đáng tin cậy. Mỗi đối số được truyền trong các chương trình cho đến nay đều được truyền theo giá trị. Khi một đối số được truyền theo tham chiếu, người gọi sẽ cung cấp cho phương thức khả năng truy cập và sửa đổi biến ban đầu của người gọi — không có bản sao nào được chuyển
Để truyền một đối tượng bằng cách tham chiếu vào một phương thức, chỉ cần cung cấp dưới dạng đối số trong phương thức gọi biến tham chiếu đến đối tượng. Sau đó, trong phần thân phương thức, hãy tham chiếu đối tượng bằng tên tham số tương ứng. Tham số đề cập đến đối tượng ban đầu trong bộ nhớ, vì vậy phương thức được gọi có thể truy cập trực tiếp vào đối tượng ban đầu
Trong phần trước, chúng ta đã bắt đầu thảo luận về sự khác biệt giữa các loại giá trị và loại tham chiếu. Một sự khác biệt lớn là
biến kiểu giá trị lưu trữ các giá trị, do đó, việc chỉ định một biến kiểu giá trị trong lệnh gọi phương thức sẽ chuyển một bản sao giá trị của biến đó sang phương thức, trong khi đó
Các biến kiểu tham chiếu lưu trữ các tham chiếu đến các đối tượng, do đó, việc chỉ định một biến kiểu tham chiếu làm đối số sẽ chuyển cho phương thức một bản sao của tham chiếu tham chiếu đến đối tượng
Mặc dù bản thân tham chiếu được truyền theo giá trị, nhưng phương thức vẫn có thể sử dụng tham chiếu mà nó nhận được để tương tác với—và có thể sửa đổi—đối tượng ban đầu. Tương tự, khi trả về thông tin từ một phương thức thông qua câu lệnh return, phương thức này trả về một bản sao của giá trị được lưu trong biến kiểu giá trị hoặc một bản sao của tham chiếu được lưu trong biến kiểu tham chiếu. Khi một tham chiếu được trả về, phương thức gọi có thể sử dụng tham chiếu đó để tương tác với đối tượng được tham chiếu
7. 18. 1 tham chiếu và tham số
Điều gì sẽ xảy ra nếu bạn muốn truyền một biến theo tham chiếu để phương thức được gọi có thể sửa đổi giá trị của biến trong trình gọi?
Tham số tham chiếu
Áp dụng từ khóa ref cho một khai báo tham số cho phép bạn chuyển một biến cho một phương thức bằng cách tham chiếu—phương thức này sẽ có thể sửa đổi biến ban đầu trong trình gọi. Từ khóa ref được sử dụng cho các biến đã được khởi tạo trong phương thức gọi
ra Thông số
Trước một tham số với từ khóa out sẽ tạo một tham số đầu ra. Điều này cho trình biên dịch biết rằng đối số sẽ được truyền vào phương thức được gọi theo tham chiếu và phương thức được gọi sẽ gán một giá trị cho biến ban đầu trong trình gọi. Điều này cũng ngăn trình biên dịch tạo thông báo lỗi cho một biến chưa được khởi tạo được truyền dưới dạng đối số cho một phương thức
Truyền các biến loại tham chiếu theo tham chiếu
Bạn cũng có thể chuyển một biến kiểu tham chiếu theo tham chiếu, điều này cho phép bạn sửa đổi nó để nó tham chiếu đến một đối tượng mới. Chuyển tham chiếu theo tham chiếu là một kỹ thuật phức tạp nhưng hiệu quả mà chúng ta sẽ thảo luận trong Phần 8. 13
7. 18. 2 Thể hiện Tham số ref, out và Giá trị
Ứng dụng trong hình. 7. 19 sử dụng từ khóa ref và out để thao tác các giá trị số nguyên. Lớp chứa ba phương thức tính bình phương của một số nguyên
1 // Fig. 7.19: ReferenceAndOutputParameters.cs 2 // Reference, output and value parameters. 3 using System; 4 5 class ReferenceAndOutputParameters 6 { 7 // call methods with reference, output and value parameters 8 static void Main[] 9 { 10 int y = 5; // initialize y to 5 11 int z; // declares z, but does not initialize it 12 13 // display original values of y and z 14 Console.WriteLine[$"Original value of y: {y}"]; 15 Console.WriteLine["Original value of z: uninitialized\n"]; 16 17 // pass y and z by reference 18 SquareRef[ref y]; // must use keyword ref 19 SquareOut[out z]; // must use keyword out 20 21 // display values of y and z after they're modified by 22 // methods SquareRef and SquareOut, respectively 23 Console.WriteLine[$"Value of y after SquareRef: {y}"]; 24 Console.WriteLine[$"Value of z after SquareOut: {z}\n"]; 25 26 // pass y and z by value 27 Square[y]; 28 Square[z]; 29 30 // display values of y and z after they're passed to method Square 31 // to demonstrate that arguments passed by value are not modified 32 Console.WriteLine[$"Value of y after Square: {y}"]; 33 Console.WriteLine[$"Value of z after Square: {z}"]; 34 } 35 36 // uses reference parameter x to modify caller's variable 37 static void SquareRef[ref int x] 38 { 39 x = x * x; // squares value of caller's variable 40 } 41 42 // uses output parameter x to assign a value 43 // to an uninitialized variable 44 static void SquareOut[out int x] 45 { 46 x = 6; // assigns a value to caller's variable 47 x = x * x; // squares value of caller's variable 48 } 49 50 // parameter x receives a copy of the value passed as an argument, 51 // so this method cannot modify the caller's variable 52 static void Square[int x] 53 { 54 x = x * x; 55 } 56 }
Original value of y: 5 Original value of z: uninitialized Value of y after SquareRef: 25 Value of z after SquareOut: 36 Value of y after Square: 25 Value of z after Square: 36
Quả sung. 7. 19. Thông số tham chiếu, đầu ra và giá trị
Phương thức SquareRef [dòng 37–40] tự nhân tham số x của nó và gán giá trị mới cho x. Tham số của SquareRef được khai báo là ref int, điều này cho biết rằng đối số được truyền cho phương thức này phải là một số nguyên được truyền theo tham chiếu. Bởi vì đối số được truyền bằng tham chiếu, phép gán ở dòng 39 sẽ sửa đổi giá trị của đối số ban đầu trong trình gọi
Phương thức SquareOut [dòng 44–48] gán cho tham số của nó giá trị 6 [dòng 46], sau đó bình phương giá trị đó. Tham số của SquareOut được khai báo là out int, điều này cho biết rằng đối số được truyền cho phương thức này phải là một số nguyên được truyền theo tham chiếu và đối số đó không cần phải khởi tạo trước
Phương thức Square [dòng 52–55] tự nhân tham số x của nó và gán giá trị mới cho x. Khi phương thức này được gọi, một bản sao của đối số được truyền cho tham số x. Do đó, mặc dù tham số x được sửa đổi trong phương thức, nhưng giá trị ban đầu trong hàm gọi không bị sửa đổi
Phương thức Chính [dòng 8–34] gọi các phương thức SquareRef, SquareOut và Square. Chúng ta bắt đầu bằng cách khởi tạo biến y thành 5 và khai báo, nhưng không khởi tạo, biến z. Dòng 18–19 gọi phương thức SquareRef và SquareOut. Lưu ý rằng khi bạn chuyển một biến cho một phương thức có tham số tham chiếu, bạn phải đặt trước đối số bằng cùng một từ khóa [ref hoặc out] đã được sử dụng để khai báo tham số tham chiếu. Các dòng 23–24 hiển thị các giá trị của y và z sau các lệnh gọi tới SquareRef và SquareOut. Lưu ý rằng y đã được thay đổi thành 25 và z đã được đặt thành 36
Dòng 27–28 gọi phương thức Square với y và z làm đối số. Trong trường hợp này, cả hai biến đều được truyền theo giá trị—chỉ các bản sao giá trị của chúng mới được truyền cho Square. Kết quả là giá trị của y và z lần lượt là 25 và 36. Các dòng 32–33 xuất các giá trị của y và z để cho thấy rằng chúng không bị sửa đổi