2009-06-07 38 views
134

Tôi hiểu rằng nếu tôi chuyển một loại giá trị (int, struct, v.v.) làm thông số (không có từ khóa ref), một bản sao của biến đó được chuyển cho phương pháp, nhưng nếu tôi sử dụng từ khóa ref để biến đó được chuyển, không phải là biến mới.Việc sử dụng "ref" cho biến kiểu tham chiếu trong C# là gì?

Nhưng với kiểu tham chiếu, như lớp học, ngay cả khi không có từ khóa ref, tham chiếu được chuyển đến phương thức chứ không phải bản sao. Vì vậy, việc sử dụng từ khóa ref với các loại tham chiếu là gì?


Đưa ví dụ:

var x = new Foo(); 

sự khác biệt giữa những điều sau là gì?

void Bar(Foo y) { 
    y.Name = "2"; 
} 

void Bar(ref Foo y) { 
    y.Name = "2"; 
} 

Trả lời

123

Bạn có thể thay đổi những gì foo điểm để sử dụng y:

Foo foo = new Foo("1"); 

void Bar(ref Foo y) 
{ 
    y = new Foo("2"); 
} 

Bar(ref foo); 
// foo.Name == "2" 
+13

nên về cơ bản bạn có được tham chiếu đến tham chiếu gốc – lhahne

+1

Bạn có thể thay đổi tham chiếu gốc 'đề cập' thành, vì vậy có. – user7116

+1

Chris, lời giải thích của bạn rất tuyệt; Cảm ơn vì đã giúp tôi hiểu khái niệm này. –

26

Có trường hợp bạn muốn thay đổi thực tế tham khảo và không phải là đối tượng được trỏ tới:

void Swap<T>(ref T x, ref T y) { 
    T t = x; 
    x = y; 
    y = t; 
} 

var test = new[] { "0", "1" }; 
Swap(ref test[0], ref test[1]); 
5

Nó cho phép bạn sửa đổi tham chiếu được chuyển vào. Ví dụ:

void Bar() 
{ 
    var y = new Foo(); 
    Baz(ref y); 
} 

void Baz(ref Foo y) 
{ 
    y.Name = "2"; 

    // Overwrite the reference 
    y = new Foo(); 
} 

Bạn cũng có thể sử dụng ra nếu bạn không quan tâm đến các tài liệu tham khảo thông qua tại:

void Bar() 
{ 
    var y = new Foo(); 
    Baz(out y); 
} 

void Baz(out Foo y) 
{ 
    // Return a new reference 
    y = new Foo(); 
} 
9

Khi bạn vượt qua một loại tài liệu tham khảo với từ khóa ref, bạn vượt qua các tài liệu tham khảo bằng cách tham khảo và phương thức bạn gọi có thể gán giá trị mới cho tham số. Thay đổi đó sẽ lan truyền đến phạm vi gọi. Không có ref, tham chiếu được truyền theo giá trị và điều này không xảy ra.

C# cũng có từ khóa 'out' rất giống ref, ngoại trừ với 'ref', đối số phải được khởi tạo trước khi gọi phương thức và với 'out' bạn phải gán giá trị trong phương thức nhận.

4

Một loạt mã

class O 
{ 
    public int prop = 0; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     O o1 = new O(); 
     o1.prop = 1; 

     O o2 = new O(); 
     o2.prop = 2; 

     o1modifier(o1); 
     o2modifier(ref o2); 

     Console.WriteLine("1 : " + o1.prop.ToString()); 
     Console.WriteLine("2 : " + o2.prop.ToString()); 
     Console.ReadLine(); 
    } 

    static void o1modifier(O o) 
    { 
     o = new O(); 
     o.prop = 3; 
    } 

    static void o2modifier(ref O o) 
    { 
     o = new O(); 
     o.prop = 4; 
    } 
} 
+2

Đầu ra: \ r \ n 1: 1 \ r \ n 2: 4 – Archie

15

Jon Skeet viết a great article về tham số đi qua trong C#. Chi tiết rõ ràng hành vi chính xác và cách sử dụng các tham số truyền theo giá trị, theo tham chiếu (ref), và theo đầu ra (out).

Dưới đây là một trích dẫn quan trọng từ trang đó liên quan đến ref thông số:

thông số tham khảo không vượt qua giá trị của các biến được sử dụng trong chức năng thành viên gọi - họ sử dụng các biến bản thân.Thay vì tạo ra một vị trí mới lưu trữ cho biến trong hàm thành viên khai, cùng vị trí lưu trữ được sử dụng, vì vậy giá trị của biến trong hàm thành viên và giá trị của tham số tham chiếu sẽ luôn được giống nhau. Tham số tham chiếu cần công cụ sửa đổi chỉnh sửa như là một phần của cả khai báo và lời gọi - rằng có nghĩa là luôn rõ ràng khi bạn đang chuyển nội dung nào đó bằng tham chiếu.

+10

Tôi thích sự tương tự của việc đưa chó xích của bạn đến bạn bè để chuyển một tham chiếu theo giá trị ... nó bị hỏng nhanh chóng, bởi vì tôi nghĩ bạn _would_ có thể thông báo nếu bạn của bạn giao dịch lên shitzu của bạn để một doberman trước khi ông đưa cho bạn trở lại dây xích ;-) – corlettk

12

Rất độc đáo giải thích ở đây: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

Tóm tắt từ bài viết:

Một biến của một loại tài liệu tham khảo không chứa dữ liệu của nó trực tiếp; nó chứa tham chiếu đến dữ liệu của nó. Khi bạn chuyển tham số kiểu tham chiếu theo giá trị, có thể thay đổi dữ liệu được chỉ dẫn bởi tham chiếu, chẳng hạn như giá trị của thành viên lớp học. Tuy nhiên, bạn không thể thay đổi giá trị của tham chiếu; nghĩa là, bạn không thể sử dụng cùng một tham chiếu để cấp phát bộ nhớ cho một lớp mới và giữ cho nó tồn tại bên ngoài khối. Để làm điều đó, hãy chuyển tham số bằng cách sử dụng từ khóa ref2 hoặc từ khóa .

+4

Giải thích thực sự là rất tốt đẹp. Tuy nhiên, câu trả lời chỉ liên kết được khuyến khích trên SO. Tôi đã thêm một bản tóm tắt từ bài viết, như một sự tiện lợi cho độc giả ở đây. – Marcel

1

Tham số trong phương pháp có vẻ luôn luôn truyền qua bản sao, câu hỏi là bản sao của nội dung. Một bản sao được thực hiện bởi một nhà xây dựng bản sao cho một đối tượng và vì tất cả các biến là Object trong C#, tôi tin rằng đây là trường hợp cho tất cả chúng. Các biến (đối tượng) giống như những người sống ở một số địa chỉ. Chúng tôi hoặc là thay đổi những người sống ở những địa chỉ đó hoặc chúng tôi có thể tạo thêm tài liệu tham khảo cho những người sống ở những địa chỉ đó trong danh bạ điện thoại (tạo bản sao nông). Vì vậy, nhiều hơn một định danh có thể tham chiếu đến cùng một địa chỉ. Các kiểu tham chiếu mong muốn nhiều không gian hơn, do đó, không giống như các kiểu giá trị được kết nối trực tiếp bởi một mũi tên tới mã định danh của chúng trong ngăn xếp, chúng có giá trị cho một địa chỉ khác trong vùng (một không gian lớn hơn để trú ngụ). Không gian này cần phải được lấy từ đống.

Value type: Indentifier (chứa value = địa chỉ của ngăn xếp giá trị) ----> Giá trị của kiểu giá trị

loại tham khảo: Identifier (chứa value = địa chỉ của ngăn xếp giá trị) ----> (chứa giá trị = địa chỉ giá trị đống) ----> Giá trị đống (thường chứa địa chỉ đến các giá trị khác), hãy tưởng tượng thêm các mũi tên bám theo các hướng khác nhau vào Mảng [0], Mảng [1], mảng [2]

Cách duy nhất để thay đổi giá trị là theo các mũi tên. Nếu một mũi tên bị mất/thay đổi theo cách giá trị không thể truy cập được.

3

Ngoài các câu trả lời có sẵn:

Như bạn hỏi cho sự khác biệt của 2 phương pháp: Không có đồng (ntra) đúng khi sử dụng ref hoặc out:

class Foo { } 
class FooBar : Foo { } 

static void Bar(Foo foo) { } 
static void Bar(ref Foo foo) { foo = new Foo(); } 

void Main() 
{ 
    Foo foo = null; 
    Bar(foo);   // OK 
    Bar(ref foo);  // OK 

    FooBar fooBar = null; 
    Bar(fooBar);  // OK (covariance) 
    Bar(ref fooBar); // compile time error 
} 
Các vấn đề liên quan