2009-07-22 18 views
8

Tôi chỉ cần thực hiện một thói quen Swap trong C# như thế này:Là ref và ra trong C# cùng một con trỏ trong C + +?

static void Swap(ref int x, ref int y) 
{ 
    int temp = x; 
    x = y; 
    y = temp; 
} 

Nó cùng một điều mà C++ mã không:

void swap(int *d1, int *d2) 
{ 
    int temp=*d1; 
    *d1=*d2; 
    *d2=temp; 
} 

Người ta có khuynh refout các từ khóa như gợi ý cho C# mà không sử dụng mã số unsafe?

Trả lời

10

Chúng bị hạn chế hơn. Bạn có thể nói ++ trên một con trỏ, nhưng không phải trên một số ref hoặc out.


EDIT Một số nhầm lẫn trong các ý kiến, vì vậy để được hoàn toàn rõ ràng: Vấn đề ở đây là so sánh với khả năng của con trỏ. Bạn không thể thực hiện thao tác tương tự như ptr++ trên ref/out, nghĩa là đặt địa chỉ đó ở vị trí lân cận trong bộ nhớ. Đó là sự thật (nhưng không liên quan ở đây) mà bạn có thể thực hiện tương đương với (*ptr)++, nhưng đó sẽ là để so sánh nó với các khả năng của giá trị, không phải con trỏ.


Đó là một cược an toàn mà họ nội chỉ con trỏ, bởi vì chồng không được di chuyển và C# được tổ chức cẩn thận để refout luôn đề cập đến một khu vực hoạt động của ngăn xếp.


EDIT Để được hoàn toàn rõ ràng một lần nữa (nếu đó là chưa rõ ràng từ các ví dụ dưới đây), các Vấn đề ở đây không phải là ref/out thể chỉ điểm vào stack. Đó là khi nó trỏ đến ngăn xếp, nó được đảm bảo bởi các quy tắc ngôn ngữ không trở thành một con trỏ lơ lửng. Đảm bảo này là cần thiết (và có liên quan/thú vị ở đây) vì ngăn xếp chỉ loại bỏ thông tin theo lối thoát cuộc gọi phương thức, không có séc để đảm bảo rằng mọi liên kết giới thiệu vẫn tồn tại.

Ngược lại khi ref/out đề cập đến các đối tượng trong đống GC, không có gì ngạc nhiên khi các đối tượng đó có thể được giữ sống miễn là cần thiết: heap GC được thiết kế chính xác cho mục đích giữ lại đối tượng trong bất kỳ khoảng thời gian nào yêu cầu bởi các liên kết giới thiệu của chúng và cung cấp tính năng ghim (xem ví dụ bên dưới) để hỗ trợ các tình huống mà đối tượng không được di chuyển bằng cách nén GC.


Nếu bạn từng chơi với interop trong mã không an toàn, bạn sẽ thấy rằng ref liên quan rất chặt chẽ đến con trỏ.Ví dụ, nếu một giao diện COM được khai báo như thế này:

HRESULT Write(BYTE *pBuffer, UINT size); 

Các Interop Assembly sẽ biến nó thành này:

void Write(ref byte pBuffer, uint size); 

Và bạn có thể làm điều này để gọi nó (tôi tin rằng những thứ COM interop chăm sóc ghim mảng):

byte[] b = new byte[1000]; 
obj.Write(ref b[0], b.Length); 

Nói cách khác, ref để byte đầu tiên giúp bạn truy cập vào tất cả; nó dường như là một con trỏ đến byte đầu tiên.

+0

Bạn có thể '+ +' trên đối số 'ref' tốt, nhưng nó không có nghĩa là giống nhau. –

+0

Ngoài ra, "' ref' và 'out' luôn đề cập đến một vùng hoạt động của ngăn xếp" là hoàn toàn sai. Ví dụ của bạn tạo một 'ref' thành một đối tượng trên gc heap. –

6

Tham số tham chiếu trong C# có thể được sử dụng để thay thế một sử dụng con trỏ, vâng. Nhưng không phải tất cả.

Sử dụng phổ biến khác cho con trỏ là một phương tiện để lặp qua một mảng. Tham số out/ref không thể làm điều đó, vì vậy không, chúng không phải là "giống như con trỏ".

1

Câu trả lời ngắn gọn là Có (chức năng tương tự, nhưng không chính xác cùng một cơ chế). Lưu ý phụ, nếu bạn sử dụng FxCop để phân tích mã của mình, sử dụng outref sẽ dẫn đến lỗi "Microsoft.Design" của "CA1045: DoNotPassTypesByReference".

1

Điều thú vị khi sử dụng ra là bạn được đảm bảo rằng mục đó sẽ được chỉ định một giá trị - bạn sẽ nhận được lỗi biên dịch nếu không.

2

Thực ra, tôi so sánh chúng với tham chiếu C++ thay vì con trỏ. Con trỏ, trong C++ và C, là một khái niệm tổng quát hơn, và các tham chiếu sẽ làm những gì bạn muốn.

Tất cả những điều này chắc chắn là con trỏ dưới bìa, tất nhiên.

+0

Về mặt cú pháp, chúng thực sự giống với con trỏ một cách chặt chẽ hơn, bởi vì bạn phải thêm vào 'ref', giống như bạn phải thêm tiền tố' & 'để có được một con trỏ trong C/C++. –

+0

(Tôi có nghĩa là tại trang web gọi điện thoại.) –

+0

Vì vậy, họ đang thực sự ở đâu đó ở giữa (vì bạn không phải dereference nó khi đọc/ghi vào nó). –

3

refout chỉ được sử dụng với đối số hàm để biểu thị rằng đối số sẽ được chuyển bằng tham chiếu thay vì giá trị. Theo nghĩa này, có, chúng có phần giống như con trỏ trong C++ (giống như các tham chiếu thực sự hơn). Đọc thêm về nó trong this article.

1

Trong khi so sánh nằm trong mắt của người xem ... Tôi nói không. 'ref' thay đổi quy ước gọi gọi nhưng không phải là loại của các tham số. Trong ví dụ C++ của bạn, d1 và d2 có kiểu int *. Trong C# chúng vẫn là Int32's, chúng chỉ xảy ra khi được chuyển qua tham chiếu thay vì bằng giá trị.

Nhân tiện, mã C++ của bạn không thực sự hoán đổi đầu vào theo nghĩa truyền thống. Hãy khái quát hóa nó như vậy:

template<typename T> 
void swap(T *d1, T *d2) 
{ 
    T temp = *d1; 
    *d1 = *d2; 
    *d2 = temp; 
} 

... sẽ không hoạt động trừ khi tất cả các loại T có nhà thầu sao chép và thậm chí sẽ kém hiệu quả hơn nhiều so với các con trỏ trao đổi.

+0

Tôi không nghĩ rằng tương tự của bạn khá hoạt động; xem xét 'int *' so với 'const int *' trong C++. Const là một vòng loại hạn chế việc sử dụng kiểu nó áp dụng, chuyển nó thành một kiểu khác. Nhưng khi C++ được biên dịch thành IL, 'const' được biến thành một loại công cụ sửa đổi tùy chỉnh trên một loại thay vì định nghĩa một kiểu khác trong hệ thống kiểu CLI. Điều này cho thấy rằng "loại" trong ngôn ngữ được tách biệt với "loại" trong thời gian chạy.Trong cùng một cách 'ref' giới hạn những gì bạn có thể làm với đối tượng (bạn không thể nắm bắt nó trong một lambda, ví dụ), và đây là một phần của hệ thống kiểu của C#, mặc dù không phải của CLI. –