2015-07-07 17 views
28

Tôi đã thực hiện một số xét nghiệm ref từ khóa và có được một suy nghĩ tôi không thể hiểu:bình đẳng tham khảo các loại giá trị

static void Test(ref int a, ref int b) 
{ 
    Console.WriteLine(Int32.ReferenceEquals(a,b)); 
} 

static void Main(string[] args) 
{ 
    int a = 4; 
    Test(ref a, ref a); 
    Console.ReadLine(); 
} 

Tại sao hiển thị mã này False? Tôi biết rằng int là một loại giá trị nhưng ở đây nó phải vượt qua các tham chiếu đến cùng một đối tượng.

+0

vì các tham chiếu không giống với các loại giá trị. –

+1

Công cụ sửa đổi 'ref' không làm đối số tương ứng được đóng hộp thành kiểu tham chiếu. – Lee

+0

Bạn đang cố gắng xem liệu hai tham số có phải là tham chiếu đến cùng một biến không? – BoltClock

Trả lời

38

Tại sao mã này hiển thị False?

int aint bare being boxed khi bạn gọi object.ReferenceEquals. Mỗi số nguyên được đóng trong một phiên bản object. Vì vậy, bạn đang thực sự so sánh tài liệu tham khảo giữa hai giá trị đóng hộp, rõ ràng là không bằng nhau.

Bạn có thể dễ dàng thấy điều này nếu bạn nhìn vào tạo CIL cho phương pháp:

Test: 
IL_0000: nop 
IL_0001: ldarg.0  Load argument a 
IL_0002: ldind.i4 
IL_0003: box   System.Int32 
IL_0008: ldarg.1  Load argument b 
IL_0009: ldind.i4 
IL_000A: box   System.Int32 
IL_000F: call  System.Object.ReferenceEquals 
IL_0014: call  System.Console.WriteLine 
IL_0019: nop 
IL_001A: ret 

Kiểm tra bình đẳng vị trí lưu trữ có thể đạt được bằng cách sử dụng có thể kiểm chứng CIL (chẳng hạn như trong @leppie's answer) hoặc bằng cách unsafe đang :

unsafe static void Main(string[] args) 
{ 
    int a = 4; 
    int b = 5; 
    Console.WriteLine(Test(ref a, ref a)); // True 
    Console.WriteLine(Test(ref a, ref b)); // False; 
} 

unsafe static bool Test(ref int a, ref int b) 
{ 
    fixed (int* refA = &a) 
    fixed (int* refB = &b) 
    { 
     return refA == refB; 
    } 
} 
+1

Điều gì sẽ xảy ra nếu bạn thay đổi một trong số chúng bên trong phương pháp? tôi nghĩ rằng điều này là không thể –

+0

@ M.kazemAkhgary Tôi không hiểu câu hỏi của bạn. –

+0

Điều gì xảy ra nếu bạn thay đổi 'a' hoặc' b' bên trong 'Kiểm tra'. Tôi có nghĩa là cả hai đều tham khảo cùng một điều ('a' trong chính). tôi có nghĩa là nếu bạn thay đổi một trong số họ, một trong những khác sẽ thay đổi quá? –

18

Điều này không thể thực hiện trực tiếp trong C#.

Bạn tuy nhiên có thể thực hiện nó trong CIL kiểm chứng:

.method public hidebysig static bool Test<T>(!!T& a, !!T& b) cil managed 
{ 
    .maxstack 8 
    ldarg.0 
    ldarg.1 
    ceq 
    ret 
} 

thử nghiệm

int a = 4, b = 4, c = 5; 
int* aa = &a; // unsafe needed for this 
object o = a, p = o; 
Console.WriteLine(Test(ref a, ref a)); // True 
Console.WriteLine(Test(ref o, ref o)); // True 
Console.WriteLine(Test(ref o, ref p)); // False 
Console.WriteLine(Test(ref a, ref b)); // False 
Console.WriteLine(Test(ref a, ref c)); // False 
Console.WriteLine(Test(ref a, ref *aa)); // True 
// all of the above works for fields, parameters and locals 

Ghi chú

này không thực sự kiểm tra các tài liệu tham khảo tương tự, nhưng thậm chí nhiều hơn chi tiết hơn ở chỗ nó đảm bảo cả hai đều giống nhau 'vị trí' (hoặc được tham chiếu từ cùng một biến). Đây là dòng thứ 3 trả về false mặc dù o == p trả lại true. Tính hữu ích của bài kiểm tra 'vị trí' này rất hạn chế.

+0

Lưu ý: đây không phải là câu trả lời, chỉ là một cách xoay vòng để đạt được nó. – leppie

+0

Xuất sắc. Tôi đã tạo ra một câu trả lời 'C#' cho điều này bằng cách sử dụng 'DynamicMethod' [ở đây] (http://stackoverflow.com/a/43570334/147511). Cảm ơn nhiều. –

2

Tôi biết, int đó là một loại giá trị nhưng ở đây nó phải chuyển tham chiếu đến cùng một đối tượng.

Vâng, tài liệu tham khảo thông qua với phương pháp là như nhau, nhưng họ đang đóng hộp (chuyển đổi sang kiểu đối tượng/tài liệu tham khảo) trong phương pháp ReferenceEquals.

Đó là lý do tại sao kết quả thử nghiệm của bạn trả về sai, vì bạn đang so sánh các tham chiếu của hai đối tượng khác nhau, do quyền anh.

Xem: Object.ReferenceEquals Method

Khi so sánh các loại giá trị.Nếu objAobjBloại giá trị, , chúng được đóng hộp trước khi chúng được chuyển đến phương pháp ReferenceEquals . Điều này có nghĩa rằng nếu cả hai objAobjB đại diện cho cùng dụ của một loại giá trị, ReferenceEquals phương pháp vẫn false trả

0

Sự rắc rối ở đây là bởi vì không giống như con trỏ (như trong *), "ref" trong C# không phải là một phần của một loại, mà là một phần của chữ ký phương thức. Nó áp dụng cho tham số và có nghĩa là "điều này không được sao chép". Nó không có nghĩa là "đối số này có kiểu tham chiếu".

Thông số được chuyển bởi ref, thay vì đại diện cho vị trí lưu trữ mới, thay vào đó là bí danh cho một số vị trí hiện tại. Cách tạo bí danh về mặt kỹ thuật là chi tiết thực hiện. Hầu hết các bí danh thường được triển khai dưới dạng tham chiếu được quản lý, nhưng không phải lúc nào cũng được. Trong một số trường hợp liên quan đến async, ví dụ, một tham chiếu đến một phần tử mảng có thể được biểu diễn bên trong như một sự kết hợp của mảng và chỉ mục.

Về cơ bản cho tất cả các mục đích, a và b của bạn vẫn được C# hiểu là biến được nhập int. Nó là hợp pháp và hoàn toàn bình thường khi sử dụng chúng trong bất kỳ biểu thức nào có giá trị int như a + b, hoặc SomeMethod (a, b) và trong những trường hợp đó giá trị int thực được lưu trữ trong a và b được sử dụng.

Thực sự không có khái niệm về "tham chiếu" như một thực thể mà bạn có thể trực tiếp làm việc với C#. Không giống như con trỏ, giá trị thực tế của tham chiếu được quản lý phải được giả định là có thể thay đổi bất kỳ lúc nào hoặc thậm chí không đồng bộ, theo GC, do đó tập hợp các kịch bản có ý nghĩa về tham chiếu được quản lý sẽ rất hạn chế.

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