2012-04-03 19 views
22

Tôi luôn nghĩ rằng tham số phương thức với loại lớp được chuyển thành tham số tham chiếu theo mặc định. Rõ ràng không phải lúc nào cũng vậy. Hãy xem xét các xét nghiệm đơn vị này trong C# (sử dụng MSTest).Việc chuyển lớp như tham số ref trong C# không phải lúc nào cũng hoạt động như mong đợi. Bất cứ ai có thể giải thích?

[TestClass] 
public class Sandbox 
{ 
    private class TestRefClass 
    { 
     public int TestInt { get; set; } 
    } 

    private void TestDefaultMethod(TestRefClass testClass) 
    { 
     testClass.TestInt = 1; 
    } 

    private void TestAssignmentMethod(TestRefClass testClass) 
    { 
     testClass = new TestRefClass() { TestInt = 1 }; 
    } 

    private void TestAssignmentRefMethod(ref TestRefClass testClass) 
    { 
     testClass = new TestRefClass() { TestInt = 1 }; 
    } 

    [TestMethod] 
    public void DefaultTest() 
    { 
     var testObj = new TestRefClass() { TestInt = 0 }; 
     TestDefaultMethod(testObj); 
     Assert.IsTrue(testObj.TestInt == 1); 
    } 

    [TestMethod] 
    public void AssignmentTest() 
    { 
     var testObj = new TestRefClass() { TestInt = 0 }; 
     TestAssignmentMethod(testObj); 
     Assert.IsTrue(testObj.TestInt == 1); 
    } 

    [TestMethod] 
    public void AssignmentRefTest() 
    { 
     var testObj = new TestRefClass() { TestInt = 0 }; 
     TestAssignmentRefMethod(ref testObj); 
     Assert.IsTrue(testObj.TestInt == 1); 
    } 
} 

Kết quả là AssignmentTest() không thành công và hai phương pháp thử khác vượt qua. Tôi cho rằng vấn đề là việc gán một cá thể mới cho tham số testClass sẽ phá vỡ tham chiếu tham số, nhưng bằng cách nào đó thêm từ khóa ref sửa lỗi này.

Có ai có thể đưa ra giải thích chi tiết, tốt về những gì đang xảy ra ở đây không? Tôi chủ yếu chỉ cố gắng mở rộng kiến ​​thức về C#; Tôi không có bất kỳ kịch bản cụ thể nào mà tôi đang cố giải quyết ...

Trả lời

29

Thứ gần như luôn luôn quên là lớp không được chuyển qua tham chiếu, tham chiếu đến lớp được chuyển theo giá trị.

Điều này quan trọng. Thay vì sao chép toàn bộ lớp (vượt qua theo giá trị theo ý nghĩa khuôn mẫu), tham chiếu cho lớp đó (Tôi đang cố gắng tránh nói "con trỏ") được sao chép. Đây là 4 hoặc 8 byte; ngon miệng hơn nhiều so với việc sao chép toàn bộ lớp và có hiệu lực có nghĩa là lớp được truyền "bằng tham chiếu".

Tại thời điểm này, phương thức có bản sao riêng của tham chiếu đến lớp. Bài tập đến tham chiếu đó được đặt trong phạm vi phương pháp (phương thức được chỉ định lại bản sao tham chiếu của chính nó). Tham khảo ý kiến ​​(như trong, nói chuyện với các thành viên trong lớp) sẽ hoạt động như bạn mong đợi: bạn sẽ thấy lớp cơ bản trừ khi bạn thay đổi nó để xem một trường hợp mới (đó là những gì bạn làm trong trường hợp thất bại của bạn) kiểm tra).

Sử dụng từ khóa ref có hiệu quả vượt qua tham chiếu bằng cách tham chiếu (con trỏ đến loại con trỏ của sự vật).

Như mọi khi, Jon Skeet đã cung cấp một cái nhìn tổng quan rất tốt bằng văn bản:

http://www.yoda.arachsys.com/csharp/parameters.html

Chú ý đến các "thông số tham khảo" phần:

thông số tham khảo không vượt qua giá trị của các biến được sử dụng trong lời gọi hàm thành viên - chúng tự sử dụng các biến.

Nếu phương pháp gán cái gì đó để tham khảo ref, sau đó sao chép của người gọi cũng bị ảnh hưởng (như bạn đã quan sát) vì họ đang tìm kiếm tại cùng tham chiếu đến một thể hiện trong bộ nhớ (như trái ngược với mỗi người có bản sao riêng của họ).

4

Quy ước mặc định cho các tham số trong C# được chuyển theo giá trị. Điều này đúng cho dù thông số là class hoặc struct. Trong trường hợp class chỉ tham chiếu được chuyển bởi giá trị trong khi trong trường hợp struct, một bản sao nông của toàn bộ đối tượng được chuyển.

Khi bạn nhập TestAssignmentMethod có tài liệu tham khảo 2 đến một đối tượng duy nhất: testObj mà sống ở AssignmentTesttestClass mà sống ở TestAssignmentMethod. Nếu bạn đã biến đổi đối tượng thực tế qua testClass hoặc testObj, nó sẽ hiển thị cho cả hai tham chiếu vì cả hai đều trỏ đến cùng một đối tượng. Trong dòng đầu tiên mặc dù bạn thực hiện

testClass = new TestRefClass() { TestInt = 1 } 

Điều này tạo ra một đối tượng mới và chỉ testClass với nó. Điều này không thay đổi nơi các điểm tham chiếu testObj theo bất kỳ cách nào vì testClass là một bản sao độc lập. Hiện tại có 2 đối tượng và 2 tham chiếu mà mỗi tham chiếu trỏ đến một cá thể đối tượng khác nhau.

Nếu bạn muốn chuyển qua ngữ nghĩa tham chiếu, bạn cần sử dụng thông số ref.

+1

Câu trả lời hay, chi tiết, nhưng vì một lý do nào đó, tôi luôn cảm thấy khó chịu khi ai đó nói "thông số luôn được truyền theo giá trị, ngay cả khi đó là tham chiếu bạn đang chuyển." Tôi không thấy sự khác biệt như thế nào lan tỏa. –

+0

@RobertHarvey Gây khó chịu, nhưng tiếc là trong trường hợp mặc định, luôn luôn đúng (trừ khi tất nhiên, 'ref' hoặc' out' đang tham dự). Tôi nghĩ lời giải thích tốt nhất mà tôi từng đọc là của Jon Skeet, trong cuốn C# của anh ấy. –

+0

@RobertHarvey thuật ngữ không may gây nhầm lẫn :( – JaredPar

2

Các AssignmentTest sử dụng TestAssignmentMethodchỉ thay đổi tham chiếu đối tượng được truyền theo giá trị.

Vì vậy, chính đối tượng được truyền theo tham chiếu nhưng tham chiếu đến đối tượng được truyền theo giá trị. vì vậy khi bạn làm:

testClass = new TestRefClass() { TestInt = 1 }; 

Bạn đang thay đổi tham chiếu sao chép địa phương truyền cho phương thức không phải là tài liệu tham khảo bạn có trong các thử nghiệm.

Vì vậy, ở đây:

[TestMethod] 
public void AssignmentTest() 
{ 
    var testObj = new TestRefClass() { TestInt = 0 }; 
    TestAssignmentMethod(testObj); 
    Assert.IsTrue(testObj.TestInt == 1); 
} 

testObj là một biến tham khảo. Khi bạn chuyển nó đến TestAssignmentMethod(testObj);, tham chiếu được chuyển theo giá trị. vì vậy khi bạn thay đổi nó trong phương pháp, tham chiếu gốc vẫn trỏ đến cùng một đối tượng.

2

2 xu của tôi

Khi lớp học được gửi tới phương thức, bản sao địa chỉ bộ nhớ của nó sẽ được gửi (hướng dẫn về nhà bạn đang được gửi). Vì vậy, bất kỳ hoạt động trên địa chỉ đó có hiệu lực nhà nhưng sẽ không thay đổi địa chỉ đó. (điều này là mặc định)

Việc chuyển lớp (đối tượng) theo tham chiếu có tác dụng truyền địa chỉ thực của nó thay vì sao chép địa chỉ. Điều đó có nghĩa là nếu bạn gán một đối tượng mới cho đối số được truyền bằng tham chiếu, nó sẽ thay đổi địa chỉ thực tế (tương tự như di chuyển). : D

Đây là cách tôi xem.

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