Tại sao C # không cho phép các biến cục bộ chỉ đọc?


115

Tranh luận thân thiện với đồng nghiệp về điều này. Chúng tôi có một số suy nghĩ về điều này, nhưng tự hỏi đám đông SO nghĩ gì về điều này?


4
@ColonelPanic C và C ++ có các biến cục bộ const mà bạn có thể khởi tạo với giá trị được tính toán trong thời gian chạy.
Crashworks

1
JavaScript 2015 (ES6) có kiểu const. Vd: {const myList = [1,2,3]; }. Thực hành lập trình rất tốt để sử dụng cấu trúc này. Thông tin thêm: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
andrew.fox

1
Đối với những người quan tâm, có một đề xuất UserVoice cho tính năng này . Nó hiện chỉ có 87 phiếu bầu, vì vậy nếu bạn muốn xem các biến địa phương chỉ đọc, vui lòng tiếp tục!
Ian Kemp

1
Nó không chỉ là một vấn đề ngôn ngữ. Đó là một vấn đề của đa số trong cộng đồng C # bao gồm cả các chuyên gia C # được xếp hạng cao nhất, rằng họ không quan tâm đến tính đúng đắn của const và bất cứ điều gì liên quan đến nó. Kháng cự là vô ích.
Patrick Fromberg

1
Cập nhật 2017 : Vui lòng bỏ phiếu cho yêu cầu tính năng được thảo luận trong repo Thiết kế ngôn ngữ C #! github.com/dotnet/csharplang/issues/188
Colonel Panic

Câu trả lời:


15

Một lý do là không có hỗ trợ CLR cho địa phương chỉ đọc. Readonly được dịch sang mã opcode CLR / CLI initonly. Cờ này chỉ có thể được áp dụng cho các trường và không có ý nghĩa đối với một cục bộ. Trên thực tế, việc áp dụng nó cho một địa phương có thể sẽ tạo ra mã không thể xác minh được.

Điều này không có nghĩa là C # không thể làm điều này. Nhưng nó sẽ cung cấp hai ý nghĩa khác nhau cho cùng một cấu trúc ngôn ngữ. Phiên bản dành cho người dân địa phương sẽ không có ánh xạ tương đương CLR.


57
Nó thực sự không liên quan gì đến sự hỗ trợ của CLI cho tính năng này, bởi vì các biến cục bộ không có cách nào được tiếp xúc với các tập hợp khác. Các readonlytừ khóa cho các lĩnh vực cần phải được hỗ trợ bởi CLI vì ảnh hưởng của nó có thể nhìn thấy hội đồng khác. Tất cả những gì nó sẽ có nghĩa là biến chỉ có một phép gán trong phương thức tại thời điểm biên dịch.
Sam Harwell

16
Tôi nghĩ bạn vừa chuyển câu hỏi sang lý do tại sao CLR không hỗ trợ điều này hơn là cung cấp lý do đằng sau nó. Nó cho phép người dân địa phương, vì vậy sẽ là hợp lý để mong đợi người dân địa phương chỉ đọc sách.
Chad Schouggins

9
Một ví dụ về điều này là biến được định nghĩa trong một câu lệnh using. Chúng là cục bộ ... và chỉ đọc (cố gắng gán chúng, C # sẽ thêm lỗi).
Softlion

7
-1 Trong C ++ không có mã máy hỗ trợ const(trong C ++ giống C # readonlyhơn là C # const, mặc dù nó có thể đóng cả hai vai trò). Tuy nhiên, C ++ hỗ trợ constcho biến tự động cục bộ. Do đó, việc thiếu hỗ trợ CLR cho C # readonlycho biến cục bộ là không thích hợp.
Chúc mừng và hth. - Alf

5
1. Đây có thể dễ dàng là một tính năng của trình biên dịch, giống như trong C ++. Hỗ trợ CLR hoàn toàn không liên quan. Máy lắp ráp cũng không hỗ trợ, vậy thì sao? 2. (nó sẽ) có khả năng tạo ra mã không thể xác minh - Tôi không hiểu bằng cách nào, nhưng có lẽ tôi đã nhầm. 3. nó sẽ cung cấp hai ý nghĩa khác nhau cho cùng một cấu trúc ngôn ngữ - Tôi nghi ngờ rằng có ai đó sẽ xem đây là một vấn đề, vìusingoutđang làm chính xác điều đó và thế giới đã không sụp đổ.
Lou

66

Tôi nghĩ rằng đó là một đánh giá kém đối với các kiến ​​trúc sư C #. sửa đổi chỉ đọc trên các biến cục bộ giúp duy trì tính đúng đắn của chương trình (giống như các xác nhận) và có thể giúp trình biên dịch tối ưu hóa mã (ít nhất là trong trường hợp của các ngôn ngữ khác). Thực tế là nó không được phép trong C # ngay bây giờ, là một lập luận khác cho rằng một số "tính năng" của C # chỉ là sự thực thi phong cách mã hóa cá nhân của người tạo ra nó.


11
Tôi đồng ý về phần "cứu lập trình viên khỏi chính anh ta", nhưng để giúp trình biên dịch tối ưu hóa mã, tôi giữ quan điểm rằng trình biên dịch có thể tìm ra rất rõ liệu một biến có thay đổi trong quá trình của phương pháp hay không và tối ưu hóa cho phù hợp một trong hai cách. Việc đặt cờ 'chỉ đọc' trước thứ gì đó mà trình tối ưu hóa nhận ra dù sao cho mục đích đó cũng không thực sự mang lại lợi ích mà còn có khả năng gây hiểu lầm.
Cornelius

1
@Cornelius Tôi đồng ý rằng có ý kiến ​​cho rằng trong một số trường hợp, trình biên dịch sử dụng sơ đồ luồng dữ liệu để tìm ra cơ hội tối ưu hóa bất kể từ khóa / công cụ sửa đổi nào. Nhưng việc cứu lập trình viên khỏi chính mình khỏi việc viết mã sai và không được tối ưu hóa một cách không cần thiết có thể mở ra cơ hội tối ưu hóa đó cho trình biên dịch.
shuva

Các trình biên dịch hiện đại vẫn không thực hiện Phép gán đơn tĩnh sao? Trong trường hợp đó, nó là một cuộc tranh luận liên quan đến việc tối ưu hóa (nhưng nếu một trình biên dịch hỗ trợ SSA thì nó có nghĩa là việc triển khai các biến cục bộ được gán một lần cũng trở nên tầm thường).
Đại

33

Giải quyết câu trả lời của Jared, nó có thể chỉ phải là một tính năng thời gian biên dịch - trình biên dịch sẽ cấm bạn ghi vào biến sau khai báo ban đầu (sẽ phải bao gồm một phép gán).

Tôi có thể thấy giá trị trong điều này không? Có thể - nhưng không phải là nhiều, thành thật mà nói. Nếu bạn không thể dễ dàng biết liệu một biến có được gán ở nơi khác trong phương thức hay không, thì phương thức của bạn quá dài.

Đối với những gì nó có giá trị, Java có tính năng này (sử dụng finalmodifier) và tôi đã rất hiếm khi nhìn thấy nó được sử dụng khác hơn trong trường hợp nó được sử dụng để cho phép các biến để được chụp bởi một lớp bên trong vô danh - và nơi mà nó được sử dụng, nó cho tôi ấn tượng về sự lộn xộn hơn là thông tin hữu ích.


75
Có một sự khác biệt giữa việc xem liệu một biến có được sửa đổi trong phương thức của bạn bằng mắt và bằng trình biên dịch hay không . Tôi không phản đối việc viết một phương thức, nêu rõ ý định của tôi là không sửa đổi một biến và yêu cầu trình biên dịch thông báo cho tôi khi tôi vô tình làm như vậy (có lẽ với lỗi đánh máy một tháng sau đó)!
A. Rex

50
Mặt khác, trong F #, tất cả các biến theo mặc định là chỉ đọc và bạn phải sử dụng từ khóa 'có thể thay đổi' nếu bạn muốn có thể thay đổi chúng. Vì F # là một ngôn ngữ .NET, tôi tưởng tượng nó thực hiện việc kiểm tra thời gian biên dịch mà bạn mô tả.
Joel Mueller

2
@ A.Rex: Câu hỏi thực sự là liệu lợi ích của việc yêu cầu trình biên dịch thực hiện việc kiểm tra đó có xứng đáng với "lông tơ" thêm khi đọc mã và thực sự không quan tâm đến nó hay không.
Jon Skeet

3
FWIW, Scala phân biệt địa phương readonly/ finalgiá trị từ các biến với từ khóa valvartừ khóa của nó . Trong mã Scala, các local valđược sử dụng rất thường xuyên (và trên thực tế, được ưu tiên hơn các local var). Tôi nghi ngờ rằng các lý do chính khiến công cụ finalsửa đổi không được sử dụng thường xuyên hơn trong Java là a) lộn xộn và b) lười biếng.
Aaron Novstrup

4
Đối với các biến cục bộ không được sử dụng trong bao đóng, readonlysẽ không quá quan trọng. Mặt khác, đối với các biến cục bộ được sử dụng trong các bao đóng, readonlytrong nhiều trường hợp sẽ cho phép trình biên dịch tạo ra mã hiệu quả hơn. Hiện tại, khi việc thực thi nhập vào một khối chứa một bao đóng, trình biên dịch phải tạo một đối tượng đống mới cho các biến đóng lại, ngay cả khi không có mã nào sử dụng bao đóng được thực thi . Nếu một biến là chỉ đọc, mã bên ngoài bao đóng có thể sử dụng một biến bình thường; chỉ khi một đại biểu được tạo để đóng ...
supercat

30

Nhóm thiết kế C # 7 đã thảo luận ngắn gọn về một đề xuất chỉ người dân địa phương và các thông số . Từ Ghi chú cuộc họp thiết kế C # cho ngày 21 tháng 1 năm 2015 :

Các tham số và địa phương có thể được lambdas nắm bắt và do đó được truy cập đồng thời, nhưng không có cách nào để bảo vệ chúng khỏi các vấn đề trạng thái chia sẻ lẫn nhau: chúng không thể chỉ đọc được.

Nói chung, hầu hết các tham số và nhiều địa phương không bao giờ được chỉ định sau khi chúng nhận được giá trị ban đầu. Việc cho phép chúng chỉ đọc sẽ thể hiện rõ ràng ý định đó.

Một vấn đề là tính năng này có thể là một "sự phiền toái hấp dẫn". Trong khi "điều đúng đắn" cần làm gần như luôn luôn là tạo các tham số và local chỉ đọc, nó sẽ làm cho mã lộn xộn đáng kể để làm như vậy.

Một ý tưởng để giảm bớt phần nào điều này là cho phép tổ hợp chỉ đọc var trên một biến cục bộ được ký hợp đồng với val hoặc một cái gì đó ngắn gọn như vậy. Nói chung, chúng ta có thể cố gắng đơn giản nghĩ về một từ khóa ngắn hơn so với từ khóa chỉ đọc đã thiết lập để thể hiện tính chỉ đọc.

Thảo luận tiếp tục trong repo Thiết kế Ngôn ngữ C #. Bỏ phiếu để thể hiện sự ủng hộ của bạn. https://github.com/dotnet/csharplang/issues/188


cũng có thể đặt chỉ đọc thành mặc định (thông qua một số tùy chọn hoặc từ khóa hoặc bất cứ điều gì). hầu hết các biến và tham số chỉ nên đọc, chỉ có một vài biến có thể ghi. và giảm thiểu số có thể ghi nói chung là một điều tốt.
Dave Cousineau

12

Đó là một sự giám sát đối với nhà thiết kế ngôn ngữ c #. F # có từ khóa val và nó dựa trên CLR. Không có lý do gì C # không thể có tính năng ngôn ngữ giống nhau.


7

Tôi là đồng nghiệp đó và nó không thân thiện! (đùa thôi)

Tôi sẽ không loại bỏ tính năng này vì tốt hơn là viết các phương thức ngắn. Nó hơi giống như nói rằng bạn không nên sử dụng các chủ đề vì chúng khó. Đưa cho tôi con dao và để tôi chịu trách nhiệm không cắt chính mình.

Cá nhân tôi muốn một từ khóa loại "var" khác như "inv" (bất biến) hoặc "rvar" để tránh lộn xộn. Tôi đã nghiên cứu F # từ cuối năm nay và thấy điều bất biến hấp dẫn.

Không bao giờ biết Java có điều này.


5

Tôi muốn các biến chỉ đọc cục bộ theo cách tương tự như tôi muốn các biến const cục bộ. Nhưng nó có ít ưu tiên hơn các chủ đề khác.
Có thể ưu tiên của nó cũng là lý do khiến các nhà thiết kế C # chưa (chưa!) Triển khai tính năng này. Nhưng nó phải dễ dàng (và tương thích ngược) để hỗ trợ các biến chỉ đọc cục bộ trong các phiên bản sau.


2

Chỉ đọc có nghĩa là nơi duy nhất mà biến cá thể có thể được đặt là trong hàm tạo. Khi khai báo một biến cục bộ, nó không có một thể hiện (nó chỉ nằm trong phạm vi) và nó không thể bị phương thức khởi tạo chạm vào.


6
Đó là nghĩa hiện tại của 'chỉ đọc' trong C #, nhưng đó không phải là câu hỏi. 'read only' có nghĩa tiếng Anh có vẻ như có ứng dụng trực quan cho một biến cục bộ: Bạn không thể ghi vào nó (sau khi nó được khởi tạo). Điều đó có vẻ rất giống ý nghĩa khi được áp dụng cho các biến cá thể, vậy tại sao (như trong phần biện minh, tôi nghĩ) chúng ta không thể áp dụng nó cho các biến cục bộ?
Spike0xff

0

Tôi biết, điều này không trả lời lý do tại sao cho câu hỏi của bạn. Dù sao, những người đọc câu hỏi này có thể đánh giá cao đoạn mã bên dưới.

Nếu bạn thực sự lo lắng về việc tự đặt mình vào chân khi ghi đè một biến cục bộ chỉ nên được đặt một lần và bạn không muốn biến nó thành một biến có thể truy cập toàn cầu hơn, bạn có thể làm như thế này.

    public class ReadOnly<T>
    {
        public T Value { get; private set; }

        public ReadOnly(T pValue)
        {
            Value = pValue;
        }

        public static bool operator ==(ReadOnly<T> pReadOnlyT, T pT)
        {
            if (object.ReferenceEquals(pReadOnlyT, null))
            {
                return object.ReferenceEquals(pT, null);
            }
            return (pReadOnlyT.Value.Equals(pT));
        }

        public static bool operator !=(ReadOnly<T> pReadOnlyT, T pT)
        {
            return !(pReadOnlyT == pT);
        }
    }

Ví dụ sử dụng:

        var rInt = new ReadOnly<int>(5);
        if (rInt == 5)
        {
            //Int is 5 indeed
        }
        var copyValueOfInt = rInt.Value;
        //rInt.Value = 6; //Doesn't compile, setter is private

Có thể không ít mã hơn rvar rInt = 5nhưng nó hoạt động.


Điều đó không giúp ích gì ở đây. Vấn đề này với biến 'var' là: {var five = 5 five = 6; Assert.That (five == 5)}
Murray

0

Bạn có thể khai báo các biến cục bộ chỉ đọc trong C #, nếu bạn đang sử dụng trình biên dịch tương tác C # csi:

>"C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe"
Microsoft (R) Visual C# Interactive Compiler version 1.3.1.60616
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> readonly var message = "hello";
> message = "goodbye";
(1,1): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

Bạn cũng có thể khai báo các biến cục bộ chỉ đọc ở .csxđịnh dạng tập lệnh.


5
Theo thông báo lỗi, messagekhông phải là một biến ở đây, nó được biên dịch thành một trường. Đây không phải là nitpicking vì sự khác biệt rõ ràng cũng tồn tại trong C # tương tác: int x; Console.WriteLine(x)là C # tương tác hợp pháp (vì xlà một trường và được khởi tạo ngầm) nhưng void foo() { int x; Console.WriteLine(x); }không phải (vì xlà một biến và được sử dụng trước khi nó được gán). Ngoài ra, Expression<Func<int>> y = x; ((MemberExpression) y.Body).Member.MemberTypesẽ tiết lộ rằng đó xthực sự là một trường chứ không phải một biến cục bộ.
Jeroen Mostert

0

c # đã có một var chỉ đọc, mặc dù theo một cú pháp hơi khác:

Hãy xem xét những dòng sau:

var mutable = myImmutableCalculationMethod();
readonly var immutable = mutable; // not allowed in C# 8 and prior versions
return immutable;

So sánh với:

var mutable = myImmutableCalculationMethod();
string immutable() => mutable; // allowed in C# 7
return immutable();

Phải thừa nhận rằng giải pháp đầu tiên có thể ít mã hơn để viết. Nhưng đoạn mã thứ 2 sẽ làm cho biến chỉ đọc rõ ràng, khi tham chiếu đến biến.


Đó không phải là "chỉ đọc". Đó là một hàm cục bộ trả về một biến đã được capture .. rất nhiều cú pháp không cần thiết và cú pháp xấu thậm chí không làm cho biến cơ bản ở chế độ chỉ đọc, từ quan điểm truy cập mã. Ngoài ra, "có thể thay đổi" thường áp dụng cho các đối tượng, không biến địa phương .. xem xét: readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");.
dùng2864740

-2

sử dụng const từ khóa để làm cho biến chỉ đọc.

tham khảo: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const

public class SealedTest
{
    static void Main()
    {
        const int c = 707;
        Console.WriteLine("My local constant = {0}", c);
    }
}

1
Chúng tôi quan tâm đến kiểu JavaScript trong constđó biến chỉ có thể được chỉ định trong quá trình khởi tạo của nó — không phải kiểu csharp constnơi chỉ có thể sử dụng các biểu thức thời gian biên dịch. Ví dụ: bạn không thể làm const object c = new object();nhưng một readonlyđịa phương sẽ cho phép bạn làm điều này.
binki

-5

Tôi nghĩ đó là bởi vì một hàm có một biến chỉ đọc có thể không bao giờ được gọi, và có thể có điều gì đó về nó sẽ vượt ra ngoài phạm vi, và khi nào bạn cần?

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.