2015-01-07 19 views
19

Nói rằng tôi có đoạn mã sau:Tham số int ref có được đóng hộp không?

void Main() 
{ 
    int a = 5; 
    f1(ref a); 
} 

public void f1(ref int a) 
{ 
    if(a > 7) return; 
    a++; 
    f1(ref a); 
    Console.WriteLine(a); 
} 

Output là:

8 8 8 

ví dụ khi chồng tháo các giá trị của tham số ref được duy trì.

Điều đó có nghĩa là thêm ref keyword vào số int parameter khiến tài khoản bị đóng hộp?
Ngăn xếp thực tế trông như thế nào trong suốt cuộc gọi đệ quy?

+11

Có thể thiếu kiến ​​thức của tôi, nhưng cho phép có đi: chuyển một loại giá trị bởi ref khiến vị trí của nó trên ngăn xếp được truyền chứ không phải là giá trị của chính nó. Nó không có gì để làm với boxing và unboxing, đúng không? – Adimeus

+5

Từ MSDN: "Không có quyền sở hữu loại giá trị khi nó được chuyển qua tham chiếu". –

Trả lời

24

Chuyển một loại giá trị theo tham chiếu gây ra vị trí trên ngăn xếp để vượt qua thay vì giá trị của chính nó. Không liên quan gì đến việc boxingunboxing. Điều này làm cho suy nghĩ về cách ngăn xếp trông trong các cuộc gọi đệ quy khá dễ dàng, như mọi cuộc gọi duy nhất đề cập đến vị trí "giống nhau" trên ngăn xếp.

Tôi nghĩ rất nhiều rắc rối đến từ MSDN's paragraph on boxing and unboxing:

Boxing là tên được đặt cho quá trình theo đó một loại giá trị được chuyển thành một loại tài liệu tham khảo. Khi bạn tạo một biến, bạn đang tạo một biến tham chiếu trỏ đến một bản sao mới trên heap. Biến tham chiếu là một đối tượng, ...

Có thể gây nhầm lẫn bạn giữa hai điều khác nhau: 1) sự "chuyển đổi" như bạn sẽ, một loại giá trị để nói một đối tượng, đó là bởi định nghĩa một loại tài liệu tham khảo :

int a = 5; 
object b = a; // boxed into a reference type 

2) với sự ra đi của một loại giá trị tham số bằng cách tham khảo:

main(){ 
    int a = 5; 
    doWork(ref a); 
} 
void doWork(ref int a) 
{ 
    a++; 
} 

Hai thứ khác nhau là gì.

+3

Có lẽ là lời giải thích tốt nhất ... Lưu ý rằng "trên ngăn xếp" là trường hợp cụ thể được đề cập trong câu hỏi, loại giá trị không phải được cấp phát trên ngăn xếp. Có thể nói "vị trí tuyệt đối (tức là trên ngăn xếp)" có thể chung chung hơn - không chắc chắn cách nó đọc cho những người không có nền C/C++. –

2

Tôi nghĩ bạn nhầm lẫn khi nói rằng tham số int đang được đóng hộp. Từ MSDN,

Boxing là quá trình chuyển đổi một loại giá trị cho các loại đối tượng hoặc cho bất kỳ loại giao diện thực hiện theo loại giá trị này

Những gì bạn có ở đây là một tham số int được thông qua bởi tham chiếu, cụ thể đó là "loại giá trị" được chuyển qua tham chiếu.

Bạn có thể tham khảo sốxuất sắc của Jon Skeet để biết thông tin chi tiết.

+0

Có, nhưng trong thời gian chạy kiểu tham số của 'f1' không phải là' System.Int32', nó được gọi là kiểu ref 'System.Int32 &', không tự báo cáo như là một kiểu giá trị. –

8

Thật dễ dàng để tạo ra một chương trình mà có thể cho kết quả khác nhau tùy thuộc vào việc ref int được đóng hộp:

static void Main() 
{ 
    int a = 5; 
    f(ref a, ref a); 
} 

static void f(ref int a, ref int b) 
{ 
    a = 3; 
    Console.WriteLine(b); 
} 

nào bạn nhận được gì? Tôi thấy 3 được in.

Quyền liên quan đến việc tạo bản sao, vì vậy nếu ref a được đóng hộp, đầu ra sẽ là 5. Thay vào đó, cả hai số ab đều là tham chiếu đến biến số a gốc trong Main. Nếu nó giúp, bạn chủ yếu có thể (không hoàn toàn) nghĩ về chúng như con trỏ.

3

Console.WriteLine(a); của bạn sẽ được thực hiện sau khi đệ quy kết thúc. Recursion được hoàn thành khi giá trị của int trở thành 8. Và để làm cho nó đến 8 nó đã đệ quy cho 3 lần. Vì vậy, sau khi cuối cùng nó sẽ in 8 và sau đó vượt qua kiểm soát để đệ quy trên đó sẽ in 8 một lần nữa như biến gọi có giá trị đã trở thành 8.

Ngoài ra kiểm tra ILDASM đầu ra

.method public hidebysig static void f1(int32& a) cil managed 
{ 
    // Code size  26 (0x1a) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: ldind.i4 
    IL_0002: ldc.i4.7 
    IL_0003: ble.s  IL_0006 
    IL_0005: ret 
    IL_0006: ldarg.0 
    **IL_0007: dup** 
    IL_0008: ldind.i4 
    IL_0009: ldc.i4.1 
    IL_000a: add 
    IL_000b: stind.i4 
    IL_000c: ldarg.0 
    IL_000d: call  void ConsoleApplication1.Program::f1(int32&) 
    IL_0012: ldarg.0 
    IL_0013: ldind.i4 
    IL_0014: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_0019: ret 
} // end of method Program::f1 
+1

Đó là tất cả chính xác, nhưng nó không trả lời bất kỳ câu hỏi của Pawan. :) – gehho

+0

Theo như tôi biết boxing không xảy ra. Nó chỉ là đẩy giá trị mới nhất trên stack. – Amit

2

Nó không phải là một boxing.

Có lời giải thích rõ ràng trong MSDN ref keyword documentation:

Đừng nhầm lẫn giữa khái niệm về đi ngang qua tham khảo với các khái niệm về các loại tài liệu tham khảo. Hai khái niệm không giống nhau. Tham số phương thức có thể được sửa đổi bởi ref bất kể đó là loại giá trị hay kiểu tham chiếu. Không có quyền sở hữu của một loại giá trị khi nó được thông qua bởi tham chiếu.

2

Thêm vào các câu trả lời hiện có cách triển khai:

CLR hỗ trợ được gọi là con trỏ được quản lý. ref chuyển con trỏ được quản lý đến biến trên ngăn xếp. Bạn cũng có thể vượt qua các vị trí heap:

var array = new int[1]; 
F(ref array[0]); 

Bạn cũng có thể chuyển tham chiếu đến các trường.

Điều này không dẫn đến việc ghim. Các con trỏ được quản lý được hiểu bởi thời gian chạy (đặc biệt là GC). Chúng có thể định vị lại. Chúng an toàn và có thể kiểm chứng được.

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