Tại sao C # không hỗ trợ trả lại tài liệu tham khảo?


141

Tôi đã đọc rằng .NET hỗ trợ trả về các tham chiếu, nhưng C # thì không. Có một lý do đặc biệt? Tại sao tôi không thể làm một cái gì đó như:

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

8
ừm ... xin trích dẫn?
RPM1984

5
C # có các loại giá trị và tham chiếu và cả hai có thể được trả về ý nghĩa của bạn.
chạy lại vào

Tôi đang nói về một cái gì đó nhưreturn ref x
Tom Sarduy

2
Sau 4 năm, bạn có thể thực hiện chính xác điều này với C# 7:)
Arghya C

Câu trả lời:


188

Câu hỏi này là chủ đề của blog của tôi vào ngày 23 tháng 6 năm 2011 . Cảm ơn vì câu hỏi tuyệt vời của bạn!

Nhóm C # đang xem xét điều này cho C # 7. Xem https://github.com/dotnet/roslyn/issues/5233 để biết chi tiết.

CẬP NHẬT: Tính năng này đã được đưa vào C # 7!


Bạn nói đúng; .NET không hỗ trợ các phương thức trả về các tham chiếu được quản lý cho các biến. .NET cũng hỗ trợ các biến cục bộ có chứa các tham chiếu được quản lý đến các biến khác. (Tuy nhiên, lưu ý rằng .NET không hỗ trợ các trường hoặc mảng có chứa các tham chiếu được quản lý cho các biến khác vì điều đó làm phức tạp quá mức câu chuyện thu gom rác. Ngoài ra, các loại "tham chiếu được quản lý cho biến" không thể chuyển đổi thành đối tượng và do đó có thể không được sử dụng như loại đối số cho kiểu hoặc phương thức chung.)

Bình luận viên "RPM1984" vì một số lý do yêu cầu trích dẫn cho thực tế này. RPM1984 Tôi khuyến khích bạn đọc đặc tả CLI Phân vùng I Phần 8.2.1.1, "Con trỏ được quản lý và các loại liên quan" để biết thông tin về tính năng này của .NET.

Hoàn toàn có thể tạo phiên bản C # hỗ trợ cả hai tính năng này. Sau đó bạn có thể làm những việc như

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

và sau đó gọi nó với

int a = 123;
int b = 456; 
ref int c = ref Max(ref a, ref b); 
c += 100;
Console.WriteLine(b); // 556!

Tôi biết theo kinh nghiệm rằng có thể xây dựng một phiên bản C # hỗ trợ các tính năng này vì tôi đã làm như vậy . Các lập trình viên tiên tiến, đặc biệt là những người chuyển mã C ++ không được quản lý, thường yêu cầu chúng tôi có thêm khả năng giống như C ++ để làm mọi việc với các tài liệu tham khảo mà không cần phải sử dụng con trỏ lớn và ghim bộ nhớ khắp mọi nơi. Bằng cách sử dụng các tài liệu tham khảo được quản lý, bạn sẽ có được những lợi ích này mà không phải trả chi phí cho việc tăng hiệu suất thu gom rác của mình.

Chúng tôi đã xem xét tính năng này và thực sự đã triển khai đủ tính năng này để hiển thị cho các nhóm nội bộ khác để nhận phản hồi của họ. Tuy nhiên tại thời điểm này dựa trên nghiên cứu của chúng tôi, chúng tôi tin rằng tính năng này không có đủ sức hấp dẫn hoặc các trường hợp sử dụng hấp dẫn để biến nó thành một tính năng ngôn ngữ được hỗ trợ thực sự . Chúng tôi có các ưu tiên cao hơn khác và thời gian và nỗ lực hạn chế, vì vậy chúng tôi sẽ không sớm thực hiện tính năng này.

Ngoài ra, thực hiện đúng cách sẽ yêu cầu một số thay đổi đối với CLR. Ngay bây giờ, CLR coi các phương thức hoàn trả là hợp pháp nhưng không thể kiểm chứng được vì chúng tôi không có máy dò phát hiện tình huống này:

ref int M1(ref int x)
{
    return ref x;
}

ref int M2()
{
    int y = 123;
    return ref M1(ref y); // Trouble!
}

int M3()
{
    ref int z = ref M2();
    return z;
}

M3 trả về nội dung của biến cục bộ của M2, nhưng thời gian tồn tại của biến đó đã kết thúc! Có thể viết một trình phát hiện xác định việc sử dụng trả lại ref rõ ràng không vi phạm an toàn ngăn xếp. Những gì chúng ta sẽ làm là viết một trình phát hiện như vậy và nếu trình phát hiện không thể chứng minh được sự an toàn của ngăn xếp, thì chúng ta sẽ không cho phép sử dụng trả về ref trong phần đó của chương trình. Nó không phải là một lượng lớn công việc phát triển để làm như vậy, nhưng nó là một gánh nặng lớn đối với các nhóm thử nghiệm để đảm bảo rằng chúng tôi thực sự có tất cả các trường hợp. Đó chỉ là một điều nữa làm tăng chi phí của tính năng đến mức ngay bây giờ lợi ích không vượt quá chi phí.

Nếu bạn có thể mô tả cho tôi tại sao bạn muốn tính năng này, tôi thực sự sẽ đánh giá cao điều đó . Chúng tôi càng có nhiều thông tin từ khách hàng thực sự về lý do tại sao họ muốn nó, càng có nhiều khả năng nó sẽ biến nó thành sản phẩm vào một ngày nào đó. Đó là một tính năng nhỏ dễ thương và tôi muốn có thể đưa nó đến khách hàng bằng cách nào đó nếu có đủ sự quan tâm.

(Xem các câu hỏi liên quan cũng Là nó có thể để trở lại một tham chiếu đến một biến trong C #?tôi có thể sử dụng một tham chiếu bên trong một hàm C # như C ++? )


4
@EricLippert: Tôi không có ví dụ thuyết phục chỉ là điều tôi đang tự hỏi. Phản hồi tuyệt vời và hấp dẫn
Tom Sarduy

1
@Eric: Trong ví dụ của bạn, sẽ không phù hợp hơn để duy trì sự y sống sau khi trở về M2? Tôi hy vọng tính năng này sẽ hoạt động giống như lambdas bắt người dân địa phương. Hoặc là hành vi bạn đề xuất vì đó là cách CLR xử lý tình huống đó?
Fede

3
@Eric: IMHO khả năng có các thuộc tính trả về các tham chiếu đến các loại giá trị là một thiếu sót lớn trong các ngôn ngữ .net. Nếu Arr là Mảng có loại giá trị (ví dụ Điểm), người ta có thể nói ví dụ Arr (3) .X = 9 và biết rằng người ta đã không thay đổi giá trị của Arr (9) .X hoặc thậm chí là một sốOtherArray (2). X; nếu Arr thay vào đó là một mảng của một số loại tham chiếu, sẽ không có sự đảm bảo nào như vậy tồn tại. Thực tế là toán tử lập chỉ mục của một mảng trả về một tham chiếu là cực kỳ hữu ích; Tôi cho rằng rất đáng tiếc rằng không có loại bộ sưu tập nào có thể cung cấp chức năng như vậy.
supercat

4
Eric, cách tốt nhất để cung cấp phản hồi liên quan đến các tình huống (như bạn đề xuất trong đoạn cuối) là gì để tối đa hóa các cơ hội mà nó đã thấy? Kết nối MS? UserVoice (với giới hạn 10 bài đăng / phiếu tùy ý)? Thứ gì khác?
Roman Starkov

1
@ThunderGr: Đó là triết lý C # cho "không an toàn" - nếu bạn viết mã có thể không an toàn cho bộ nhớ thì C # khẳng định rằng bạn đánh dấu nó là "không an toàn", do đó bạn phải chịu trách nhiệm về an toàn bộ nhớ. C # đã có phiên bản không an toàn của tính năng, với điều kiện là biến được đề cập là loại không được quản lý. Câu hỏi đặt ra là liệu nhóm C # có nên tạo một phiên bản không an toàn để xử lý các loại được quản lý hay không. Nếu làm như vậy giúp nhà phát triển dễ dàng viết các lỗi khủng khiếp, điều đó sẽ không xảy ra. C # không phải là C ++, một ngôn ngữ giúp dễ dàng viết các lỗi khủng khiếp. C # là an toàn theo thiết kế.
Eric Lippert

21

Bạn đang nói về các phương thức trả về một tham chiếu đến một loại giá trị. Ví dụ tích hợp duy nhất trong C # mà tôi biết là trình truy cập mảng của một loại giá trị:

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

và bây giờ tạo một mảng của cấu trúc đó:

var points = new Point[10];
points[0].X = 1;
points[0].Y = 2;

Trong trường hợp này points[0], bộ chỉ mục mảng , đang trả về một tham chiếu đến struct. Không thể viết bộ chỉ mục của riêng bạn (ví dụ cho bộ sưu tập tùy chỉnh), có cùng hành vi "trả lại tham chiếu" này.

Tôi đã không thiết kế ngôn ngữ C # vì vậy tôi không biết tất cả lý do đằng sau việc không hỗ trợ nó, nhưng tôi nghĩ rằng câu trả lời ngắn gọn có thể là: chúng ta có thể hòa thuận với nhau mà không cần đến nó.


8
Tom đang hỏi về các phương thức trả về một tham chiếu đến một biến . Biến không cần phải là loại giá trị, mặc dù tất nhiên đó thường là những gì mọi người muốn khi họ muốn các phương thức trả lại. Mặt khác, phân tích tuyệt vời; bạn là chính xác rằng nơi duy nhất trong ngôn ngữ C #, nơi một biểu hiện phức tạp tạo ra một ref cho một biến mà người dùng sau đó có thể thao tác là indexer mảng. (Và tất nhiên là toán tử truy cập thành viên "." Giữa người nhận và trường, nhưng đó rõ ràng là quyền truy cập vào một biến.)
Eric Lippert

1

Bạn luôn có thể làm một cái gì đó như:

public delegate void MyByRefConsumer<T>(ref T val);

public void DoSomethingWithValueType(MyByRefConsumer<int> c)
{
        int x = 2;
        c(ref x);
        //Handle potentially changed x...
}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.