Tại sao tham số const không được phép trong C #?


102

Nó trông kỳ lạ đặc biệt đối với các nhà phát triển C ++. Trong C ++, chúng ta đã sử dụng để đánh dấu một tham số constđể đảm bảo rằng trạng thái của nó sẽ không bị thay đổi trong phương thức. Ngoài ra còn có các lý do cụ thể khác của C ++, chẳng hạn như chuyển const refđể chuyển qua ref và chắc chắn rằng trạng thái đó sẽ không bị thay đổi. Nhưng tại sao chúng ta không thể đánh dấu là tham số phương thức const trong C #?

Tại sao tôi không thể khai báo phương thức của mình như sau?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....

Nó luôn luôn chỉ là một tính năng bắt an toàn trong C ++ và không bao giờ thực sự tích hợp với ngôn ngữ như tôi đã thấy. Các nhà phát triển C # không nghĩ rằng nó đã tăng thêm nhiều giá trị một cách rõ ràng.
Noldorin

3
Một cuộc thảo luận lớn hơn về câu hỏi này: "Const-
trueness

Có cho các loại giá trị [out / ref], nhưng không có các loại tham chiếu. Đó là một câu hỏi thú vị.
kenny

4
Làm thế nào câu hỏi này có thể có cả thẻ C # và thẻ ngôn ngữ bất khả tri?
CJ7

1
Dữ liệu bất biến và các chức năng không có tác dụng phụ dễ lý giải hơn. Bạn có thể thưởng thức F # fsharpforfunandprofit.com/posts/is-your-language-unrerational
Colonel Panic

Câu trả lời:


59

Ngoài các câu trả lời hay khác, tôi sẽ thêm một lý do khác tại sao không đặt hằng số kiểu C vào C #. Bạn đã nói:

chúng tôi đánh dấu tham số là const để chắc chắn rằng trạng thái của nó sẽ không bị thay đổi trong phương thức.

Nếu const thực sự đã làm điều đó, điều đó sẽ rất tuyệt. Const không làm điều đó. Const là một lời nói dối!

Const không cung cấp bất kỳ đảm bảo rằng tôi thực sự có thể sử dụng. Giả sử bạn có một phương thức nhận một điều là const. Có hai mã tác giả: người viết gọi và người viết callee . Tác giả của callee đã làm cho phương thức có một const. Hai tác giả có thể cho rằng điều gì là bất biến về đối tượng?

Không có gì. Callee có thể tự do loại bỏ const và biến đổi đối tượng, vì vậy người gọi không có gì đảm bảo rằng việc gọi một phương thức nhận const thực sự sẽ không làm thay đổi nó. Tương tự như vậy, callee không thể giả định rằng nội dung của đối tượng sẽ không thay đổi trong suốt hành động của callee; callee có thể gọi một số phương thức thay đổi trên một bí danh không phải const của đối tượng const, và bây giờ cái gọi là đối tượng const đã thay đổi .

C-style const không cung cấp đảm bảo rằng đối tượng sẽ không thay đổi, và do đó bị phá vỡ. Bây giờ, C đã có một hệ thống kiểu yếu, trong đó bạn có thể diễn giải lại một hàm kép thành một int nếu bạn thực sự muốn, vì vậy không có gì ngạc nhiên khi nó cũng có một hệ thống kiểu yếu đối với const. Nhưng C # được thiết kế để có một hệ thống kiểu tốt , một hệ thống kiểu mà khi bạn nói "biến này chứa một chuỗi" thì biến đó thực sự chứa một tham chiếu đến một chuỗi (hoặc null). Chúng tôi hoàn toàn không muốn đặt một bổ ngữ "const" kiểu C vào hệ thống loại bởi vì chúng tôi không muốn hệ thống loại là một sự lừa dối . Chúng tôi muốn hệ thống loại phải mạnh để bạn có thể suy luận chính xác về mã của mình.

Const in C là một kim chỉ nam ; về cơ bản nó có nghĩa là "bạn có thể tin tưởng tôi để không cố gắng thay đổi điều này". Điều đó không nên có trong hệ thống loại ; nội dung trong hệ thống kiểu phải là sự thật về đối tượng mà bạn có thể suy luận, không phải là một hướng dẫn sử dụng nó.

Bây giờ, đừng hiểu lầm tôi; chỉ vì const trong C bị phá vỡ sâu sắc không có nghĩa là toàn bộ khái niệm là vô dụng. Những gì tôi muốn thấy là một số dạng chú thích "const" thực sự chính xáchữu ích trong C #, một chú thích mà cả con người và trình biên dịch đều có thể sử dụng để giúp họ hiểu mã và rằng thời gian chạy có thể sử dụng để thực hiện những việc như phân tích cú pháp tự động và tối ưu hóa nâng cao khác.

Ví dụ, hãy tưởng tượng nếu bạn có thể "vẽ một cái hộp" xung quanh một đoạn mã và nói "Tôi đảm bảo rằng đoạn mã này không thực hiện đột biến đối với bất kỳ trường nào của lớp này" theo cách mà trình biên dịch có thể kiểm tra được. Hoặc vẽ một hộp có nội dung " phương pháp thuần túy này làm thay đổi trạng thái bên trong của đối tượng nhưng không theo bất kỳ cách nào có thể quan sát được bên ngoài hộp". Một đối tượng như vậy không thể tự động đa luồng một cách an toàn nhưng nó có thể được tự động ghi nhớ . Có tất cả các loại chú thích thú vị mà chúng tôi có thể đặt trên mã để cho phép tối ưu hóa phong phú và hiểu sâu hơn. Chúng ta có thể làm theo cách tốt hơn là chú thích const kiểu C yếu.

Tuy nhiên, tôi nhấn mạnh rằng đây chỉ là suy đoán . Chúng tôi không có kế hoạch chắc chắn để đưa loại tính năng này vào bất kỳ phiên bản giả định nào trong tương lai của C #, nếu thậm chí có một tính năng mà chúng tôi chưa công bố theo cách này hay cách khác. Đó là thứ mà tôi rất muốn thấy, và thứ mà sự nhấn mạnh sắp tới về điện toán đa lõi có thể yêu cầu, nhưng không có điều gì trong số này nên được hiểu là một dự đoán hoặc đảm bảo cho bất kỳ tính năng cụ thể hoặc hướng tương lai nào cho C #.

Bây giờ, nếu những gì bạn muốn chỉ là một chú thích trên biến cục bộ là một tham số cho biết "giá trị của tham số này không thay đổi trong suốt phương thức", thì chắc chắn, điều đó sẽ được thực hiện dễ dàng. Chúng tôi có thể hỗ trợ các địa phương và tham số "chỉ đọc" sẽ được khởi tạo một lần và lỗi thời gian biên dịch cần thay đổi trong phương thức. Biến được khai báo bởi câu lệnh "using" đã là một biến cục bộ; chúng tôi có thể thêm một chú thích tùy chọn cho tất cả các local và tham số để làm cho chúng hoạt động giống như các biến "sử dụng". Nó chưa bao giờ là một tính năng có mức độ ưu tiên rất cao nên nó chưa bao giờ được triển khai.


34
Xin lỗi, nhưng tôi thấy lập luận này rất yếu, việc một nhà phát triển có thể nói dối không thể bị ngăn chặn bằng bất kỳ ngôn ngữ nào. void foo (const A & a) sửa đổi đối tượng a không khác với int countApples (Giỏ b) trả về số lượng lê. Nó chỉ là một lỗi, một lỗi biên dịch trong bất kỳ ngôn ngữ nào.
Alessandro Teruzzi

55
“Callee có thể tự do loại bỏ const…” uhhhhh… (° _ °) Đây là một lập luận khá nông cạn mà bạn đang đưa ra ở đây. Callee cũng miễn phí chỉ bắt đầu ghi các vị trí bộ nhớ ngẫu nhiên với 0xDEADBEEF. Nhưng cả hai sẽ là một chương trình rất tệ và bị bắt sớm và sâu sắc bởi bất kỳ trình biên dịch hiện đại nào. Trong tương lai khi viết câu trả lời, xin đừng nhầm lẫn giữa những gì có thể về mặt kỹ thuật bằng cách sử dụng backdoor của một ngôn ngữ với những gì khả thi trong mã thế giới thực. Thông tin sai lệch như thế này gây nhầm lẫn cho người đọc ít kinh nghiệm và làm giảm chất lượng của thông tin trên SO.
Slipp D. Thompson

8
"Const in C là một kim chỉ nam;" Trích dẫn một nguồn cho một số điều bạn đang nói sẽ thực sự hữu ích cho trường hợp của bạn ở đây. Theo kinh nghiệm giáo dục và công nghiệp của tôi, câu lệnh được trích dẫn là hogwash— const trong C có nhiều tính năng bắt buộc được tích hợp sẵn như chính hệ thống kiểu — cả hai đều có cửa sau để lừa chúng khi cần thiết (giống như mọi thứ trong C) nhưng được mong đợi được sử dụng bởi cuốn sách với 99,99% mã.
Slipp D. Thompson

29
Câu trả lời này là lý do tại sao đôi khi tôi ghét thiết kế C #. Các nhà thiết kế ngôn ngữ hoàn toàn chắc chắn rằng họ thông minh hơn nhiều so với các nhà phát triển khác và cố gắng bảo vệ họ khỏi lỗi. Rõ ràng là từ khóa const chỉ hoạt động trong thời gian biên dịch, vì trong C ++ chúng ta có quyền truy cập trực tiếp vào bộ nhớ và có thể làm bất cứ điều gì chúng ta muốn và có một số cách để khai báo const vòng. Nhưng không ai làm điều này trong những tình huống bình thường. Nhân tiện, 'riêng tư' và 'được bảo vệ' trong C # cũng không có gì đảm bảo, vì chúng ta có thể truy cập các phương thức hoặc trường này bằng cách sử dụng phản chiếu.
BlackOverlord

13
@BlackOverlord: (1) Mong muốn thiết kế một ngôn ngữ có các đặc tính tự nhiên dẫn các nhà phát triển đến thành công hơn là thất bại được thúc đẩy bởi mong muốn xây dựng các công cụ hữu ích , không phải bởi niềm tin rằng các nhà thiết kế thông minh hơn khách hàng của họ, như bạn phỏng đoán. (2) Phản xạ không phải là một phần của ngôn ngữ C #; nó là một phần của thời gian chạy; quyền truy cập vào phản ánh bị giới hạn bởi hệ thống bảo mật của thời gian chạy. Mã có độ tin cậy thấp không được cấp khả năng thực hiện phản ánh riêng tư. Các đảm bảo được cung cấp bởi các công cụ sửa đổi quyền truy cập dành cho mã có độ tin cậy thấp ; mã tin cậy cao có thể làm bất cứ điều gì.
Eric Lippert

24

Một trong những lý do tại sao không có tính đúng const trong C # là vì nó không tồn tại ở mức thời gian chạy. Hãy nhớ rằng C # 1.0 không có bất kỳ tính năng nào trừ khi nó là một phần của thời gian chạy.

Và một số lý do tại sao CLR không có khái niệm về tính đúng của const là ví dụ:

  1. Nó làm phức tạp thời gian chạy; bên cạnh đó, JVM cũng không có nó, và CLR về cơ bản bắt đầu như một dự án để tạo thời gian chạy giống JVM, không phải thời gian chạy giống như C ++.
  2. Nếu có const đúng ở mức thời gian chạy, thì phải có const đúng trong BCL, nếu không thì tính năng này hoàn toàn vô nghĩa đối với .NET Framework.
  3. Nhưng nếu BCL yêu cầu tính đúng của const thì mọi ngôn ngữ trên CLR phải hỗ trợ tính đúng của const (VB, JavaScript, Python, Ruby, F #, v.v.) Điều đó sẽ không xảy ra.

Tính đúng của Const là một tính năng ngôn ngữ chỉ có trong C ++. Vì vậy, nó tóm gọn lại cùng một lập luận về lý do tại sao CLR không yêu cầu các ngoại lệ đã kiểm tra (đó là một tính năng chỉ dành cho ngôn ngữ Java).

Ngoài ra, tôi không nghĩ rằng bạn có thể giới thiệu một tính năng hệ thống kiểu cơ bản như vậy trong môi trường được quản lý mà không phá vỡ khả năng tương thích ngược. Vì vậy, đừng tin tưởng vào độ đúng của const từng bước vào thế giới C #.


Bạn có chắc rằng JVM không có nó. Như tôi biết nó có nó. Bạn có thể thử public static void TestMethod (final int val) trong Java.
Incognito,

2
Cuối cùng không thực sự là const. Bạn vẫn có thể thay đổi các trường trên IIRC tham chiếu, chỉ là bản thân giá trị / tham chiếu không. (Đó là thông qua giá trị dù sao, do đó, nó nhiều hơn một thủ thuật trình biên dịch để ngăn cản bạn từ việc thay đổi giá trị tham số.)
Ruben

1
gạch đầu dòng thứ 3 của bạn chỉ đúng một phần. có tham số const cho các cuộc gọi BCL sẽ không phải là một thay đổi đột phá vì chúng chỉ mô tả hợp đồng chính xác hơn. "Phương pháp này sẽ không thay đổi trạng thái của đối tượng" trái ngược với "Phương pháp này yêu cầu bạn phải vượt qua một giá trị const" mà sẽ được phá vỡ
Rune FS

2
Nó được thực thi bên trong phương thức nên sẽ không phải là một thay đổi đột phá khi giới thiệu nó trong BCL.
Rune FS

1
Ngay cả khi thời gian chạy không thể thực thi nó, sẽ rất hữu ích nếu có một thuộc tính chỉ định liệu một hàm có được phép / dự kiến ​​ghi vào một reftham số hay không (đặc biệt, trong trường hợp các phương thức / thuộc tính struct, this). Nếu một phương thức nói rõ rằng nó sẽ không ghi vào một reftham số, thì trình biên dịch sẽ cho phép các giá trị chỉ đọc được truyền. Ngược lại, nếu một phương thức nói rằng nó sẽ ghi this, trình biên dịch sẽ không cho phép một phương thức như vậy được gọi trên các giá trị chỉ đọc.
supercat

12

Tôi tin rằng có hai lý do khiến C # không đúng.

Đầu tiên là tính dễ hiểu . Rất ít lập trình viên C ++ hiểu được hằng số đúng. Ví dụ đơn giản về const int argdễ thương, nhưng tôi cũng đã thấy char * const * const arg- một con trỏ liên tục đến các con trỏ liên tục trỏ đến các ký tự không hằng số. Hằng số đúng trên con trỏ đến các hàm là một cấp độ hoàn toàn mới của sự xáo trộn.

Thứ hai là vì các đối số của lớp là các tham chiếu được truyền theo giá trị. Điều này có nghĩa là đã có hai cấp độ hằng số cần xử lý, mà không có cú pháp rõ ràng . Một điểm vấp tương tự là các bộ sưu tập (và bộ sưu tập các bộ sưu tập, v.v.).

Tính đúng hằng là một phần quan trọng của hệ thống kiểu C ++. Về lý thuyết, nó có thể được thêm vào C # như một thứ chỉ được kiểm tra tại thời điểm biên dịch (nó không cần phải được thêm vào CLR và sẽ không ảnh hưởng đến BCL trừ khi khái niệm về phương thức thành viên const được bao gồm) .

Tuy nhiên, tôi tin rằng điều này khó xảy ra: lý do thứ hai (cú pháp) sẽ khá khó giải quyết, điều này sẽ làm cho lý do đầu tiên (tính dễ hiểu) thậm chí còn là một vấn đề.


1
Bạn nói đúng rằng CLR không thực sự cần phải lo lắng về điều này, vì người ta có thể sử dụng modoptmodreqở cấp siêu dữ liệu để lưu trữ thông tin đó (như C + / CLI đã làm - xem System.Runtime.CompilerServices.IsConst); và điều này có thể được mở rộng một cách đáng kể đối với các phương thức const.
Pavel Minaev

Nó là đáng giá nhưng cần phải được thực hiện một cách an toàn. Hằng số Sâu / Nông mà bạn đề cập sẽ mở ra cả một đống sâu.
user1496062

Trong một c ++ nơi các tham chiếu thực sự được thao tác, tôi có thể thấy lập luận của bạn về việc có hai loại const. Tuy nhiên, trong C # (nơi bạn phải đi qua cửa sau để sử dụng "con trỏ") đối với tôi, dường như đủ rõ ràng rằng có một ý nghĩa được xác định rõ ràng cho các loại tham chiếu (tức là các lớp) và một ý nghĩa được xác định rõ mặc dù ý nghĩa khác cho giá trị loại (tức là nguyên thủy, cấu trúc)
Assimilater

2
Các lập trình viên không thể hiểu const-ness sẽ không nên lập trình bằng C ++.
Steve White,

5

constcó nghĩa là "hằng số thời gian biên dịch" trong C #, không phải "chỉ đọc nhưng có thể thay đổi bởi mã khác" như trong C ++. Một tương tự thô của C ++ consttrong C # là readonly, nhưng tương tự đó chỉ áp dụng cho các trường. Bên cạnh đó, không có C ++ - giống như khái niệm về tính đúng của const trong C # cả.

Khó có thể nói chắc chắn về cơ sở lý do, bởi vì có rất nhiều lý do tiềm ẩn, nhưng tôi đoán trước hết là mong muốn giữ cho ngôn ngữ đơn giản và quan trọng nhất, và những lợi thế không chắc chắn của việc triển khai thứ hai này.


Vì vậy, bạn nghĩ MS coi như 'tiếng ồn' để có tham số const trong các phương thức.
Incognito

1
Tôi không chắc "tiếng ồn" là gì - tôi thà gọi nó là "tỷ lệ chi phí / lợi ích cao". Nhưng, tất nhiên, bạn sẽ cần một người nào đó từ đội C # nói cho bạn biết chắc chắn.
Pavel Minaev

Vì vậy, khi tôi hiểu nó, lập luận của bạn là hằng số đã được loại bỏ khỏi C # để giữ cho nó đơn giản. Nghĩ về điều đó.
Steve White,

2

Điều này có thể thực hiện được kể từ C # 7.2 (tháng 12 năm 2017 với Visual Studio 2017 15.5).

Chỉ dành cho cấu trúc và các loại cơ bản ! , không dành cho thành viên của các lớp.

Bạn phải sử dụng inđể gửi đối số dưới dạng đầu vào bằng tham chiếu. Xem:

Xem: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

Ví dụ của bạn:

....
static void TestMethod1(in MyStruct val)
{
    val = new MyStruct;  // Error CS8331 Cannot assign to variable 'in MyStruct' because it is a readonly variable
    val.member = 0;  // Error CS8332 Cannot assign to a member of variable 'MyStruct' because it is a readonly variable
}
....
static void TestMethod2(in int val)
{
    val = 0;  // Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable
}
....
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.