2012-04-25 43 views
10

Đây là ví dụ từ một cuốn sách C# mà tôi đang đọc chỉ gặp một chút rắc rối khi hiểu ví dụ này thực sự đang làm gì, giải thích để giúp tôi hiểu thêm về những gì đang xảy ra ở đây.Truyền các mảng bằng giá trị và tham khảo

 //creates and initialzes firstArray 
     int[] firstArray = { 1, 2, 3 }; 

     //Copy the reference in variable firstArray and assign it to firstarraycopy 
     int[] firstArrayCopy = firstArray; 

     Console.WriteLine("Test passing firstArray reference by value"); 


     Console.Write("\nContents of firstArray " + 
      "Before calling FirstDouble:\n\t"); 

     //display contents of firstArray with forloop using counter 
     for (int i = 0; i < firstArray.Length; i++) 
      Console.Write("{0} ", firstArray[i]); 

     //pass variable firstArray by value to FirstDouble 
     FirstDouble(firstArray); 

     Console.Write("\n\nContents of firstArray after " + 
      "calling FirstDouble\n\t"); 

     //display contents of firstArray 
     for (int i = 0; i < firstArray.Length; i++) 
      Console.Write("{0} ", firstArray[i]); 

     // test whether reference was changed by FirstDouble 
     if (firstArray == firstArrayCopy) 
      Console.WriteLine(
       "\n\nThe references refer to the same array"); 
     else 
      Console.WriteLine(
       "\n\nThe references refer to different arrays"); 

     //method firstdouble with a parameter array 
     public static void FirstDouble(int[] array) 
    { 
     //double each elements value 
     for (int i = 0; i < array.Length; i++) 
      array[i] *= 2; 

     //create new object and assign its reference to array 
     array = new int[] { 11, 12, 13 }; 

Về cơ bản có mã những gì tôi muốn biết là cuốn sách đang nói nếu mảng được truyền theo giá trị hơn người gọi ban đầu không được sửa đổi theo phương pháp (từ những gì tôi hiểu). Vì vậy, đối với sự kết thúc của phương pháp FirstDouble họ cố gắng và gán mảng biến cục bộ cho một tập hợp các phần tử mới thất bại và các giá trị mới của người gọi ban đầu khi được hiển thị là 2,4,6.

Bây giờ sự nhầm lẫn của tôi là cách vòng lặp for trong phương thức FirstDouble thay đổi người gọi ban đầu firstArray thành 2,4,6 nếu nó được truyền theo giá trị. Tôi nghĩ rằng giá trị nên vẫn còn 1,2,3.

Cảm ơn trước

+0

bản sao có thể có của [Loại giá trị và vấn đề loại tham chiếu] (http://stackoverflow.com/questions/6070892/value-type-and-reference-type-problem) –

+0

@AlexeiLevenkov Với điều này được đề cập ở những nơi khác, tôi 'd ghét để đóng cho rằng một: ( –

Trả lời

30

Chìa khóa để hiểu biết này là để biết sự khác biệt giữa một value type and a reference type.

Ví dụ: hãy xem xét loại giá trị điển hình, int.

int a = 1; 
int b = a; 
a++; 

Sau khi mã này đã thực hiện, a có giá trị 2, và b có giá trị 1. Bởi vì int là một loại giá trị, b = a lấy một bản sao giá trị của a.

Bây giờ hãy xem xét một lớp:

MyClass a = new MyClass(); 
a.MyProperty = 1; 
MyClass b = a; 
a.MyProperty = 2; 

Bởi vì lớp nhiều loại tài liệu tham khảo, b = a chỉ gán tài liệu tham khảo chứ không phải là giá trị. Vì vậy, ba đều tham chiếu đến cùng một đối tượng. Do đó, sau khi thực hiện a.MyProperty = 2, b.MyProperty == 2 kể từ ab tham chiếu đến cùng một đối tượng.


Xét mã trong câu hỏi của bạn, một mảng là một loại tài liệu tham khảo và do đó cho chức năng này:

public static void FirstDouble(int[] array) 

biến array thực sự là một tài liệu tham khảo, vì int[] là một loại tài liệu tham khảo. Vì vậy, array là tham chiếu được chuyển theo giá trị.

Do đó, các sửa đổi được thực hiện cho array bên trong hàm thực sự được áp dụng cho đối tượng int[]array tham chiếu. Và những thay đổi đó được hiển thị cho tất cả các tham chiếu tham chiếu đến cùng một đối tượng đó. Và bao gồm tham chiếu mà người gọi giữ.

Bây giờ, nếu chúng ta nhìn vào việc thực hiện chức năng này:

public static void FirstDouble(int[] array) 
{ 
    //double each elements value 
    for (int i = 0; i < array.Length; i++) 
     array[i] *= 2; 

    //create new object and assign its reference to array 
    array = new int[] { 11, 12, 13 }; 
} 

có một biến chứng hơn nữa. Vòng lặp for chỉ đơn giản là tăng gấp đôi mỗi phần tử của int[] được chuyển đến hàm. Đó là sửa đổi mà người gọi thấy.Phần thứ hai là gán một đối tượng int[] mới cho biến cục bộ array. Điều này không hiển thị với người gọi vì tất cả những gì nó làm là thay đổi đích của tham chiếu array. Và vì tham chiếu array được truyền theo giá trị, người gọi không thấy đối tượng mới đó.

Nếu chức năng đã được công bố như thế này:

public static void FirstDouble(ref int[] array) 

sau đó tham chiếu array sẽ được thông qua tham khảo và người gọi sẽ thấy đối tượng mới được tạo ra { 11, 12, 13 } khi chức năng trả lại.

+0

Vì vậy, sự khác biệt từ làm 'public static void FirstDouble (ref int [] mảng) là gì? – Jmyster

+0

@ Jmyster bản cập nhật mới nhất của tôi bao gồm điều đó. –

+0

Không có gì sai khi nói" biến mảng thực sự là một tham chiếu ". Điều đó có nghĩa là gán cho nó sẽ thay đổi giá trị trong người gọi khi sử dụng thuật ngữ gọi theo tham chiếu. Việc giữ cụm từ khó hiểu" tham chiếu "này sẽ khó nói về các tham chiếu trong C++ hoặc 'ref/out' trong C#, đó là lý do tại sao nó nhận được -1 của tôi –

2

Tất cả thông số phương pháp đều được chuyển theo giá trị trừ khi bạn thấy cụ thể ref hoặc out.

Mảng là loại tham chiếu. Điều này có nghĩa là bạn đang chuyển một tham chiếu theo giá trị.

Bản thân tham chiếu chỉ thay đổi khi bạn gán một mảng mới cho nó, đó là lý do tại sao các nhiệm vụ đó không được phản ánh trong người gọi. Khi bạn de-tham chiếu đối tượng (mảng ở đây) và sửa đổi giá trị cơ bản bạn không thay đổi biến, chỉ là những gì nó trỏ đến. Sự thay đổi này cũng sẽ được "nhìn thấy" bởi người gọi, mặc dù biến số (tức là nó thay đổi) vẫn không thay đổi.

1

Điều gì gây nhầm lẫn khi sử dụng cụm từ!

Để làm rõ,

1) cho một foo phương pháp (int [] myArray), "đi qua một tài liệu tham khảo (object) bằng giá trị" thực sự có nghĩa là "đi qua một bản sao của địa chỉ của đối tượng (tham khảo)". Giá trị của 'bản sao' này, tức là. myArray, ban đầu là Địa chỉ (tham chiếu) của đối tượng gốc, có nghĩa là nó trỏ đến đối tượng gốc. Do đó, bất kỳ thay đổi nào đối với nội dung được chỉ định bởi myArray sẽ ảnh hưởng đến nội dung của đối tượng gốc.

Tuy nhiên, vì 'giá trị' của bản thân myArray là bản sao, bất kỳ thay đổi nào đối với 'giá trị' này sẽ không ảnh hưởng đến đối tượng gốc cũng như nội dung của nó.

2) cho phương thức foo (ref int [] refArray), "truyền tham chiếu (đối tượng) bằng tham chiếu" có nghĩa là "chuyển địa chỉ của đối tượng (tham chiếu) chính nó (không phải bản sao)". Điều đó có nghĩa là refArray thực sự là địa chỉ gốc của đối tượng, chứ không phải bản sao. Do đó, bất kỳ thay đổi nào đối với 'giá trị' của refArray, hoặc nội dung được chỉ định bởi refArray là một sự thay đổi trực tiếp trên chính đối tượng ban đầu.

Các vấn đề liên quan