"Const đúng độ" trong C #


79

Điểm của const-đúng là có thể cung cấp chế độ xem của một cá thể mà người dùng không thể thay đổi hoặc xóa. Trình biên dịch hỗ trợ điều này bằng cách chỉ ra khi bạn phá vỡ hằng số từ bên trong một hàm const, hoặc cố gắng sử dụng một hàm không phải const của một đối tượng const. Vì vậy, không cần sao chép cách tiếp cận const, có phương pháp nào tôi có thể sử dụng trong C # có cùng kết thúc không?

Tôi biết về tính bất biến, nhưng điều đó không thực sự chuyển sang các đối tượng chứa để đặt tên mà là một ví dụ.


Có một cuộc thảo luận thú vị về các thiết kế có thể có để chỉ đọc tại http://blogs.msdn.com/brada/archive/2004/02/04/67859.aspx
Asaf R

Sau đó, những gì về triển khai const_casttrong C #?
kerem

Câu trả lời:


64

Tôi cũng đã gặp vấn đề này rất nhiều lần và cuối cùng đã sử dụng giao diện.

Tôi nghĩ điều quan trọng là phải bỏ ý tưởng rằng C # là bất kỳ dạng nào, hoặc thậm chí là một sự phát triển của C ++. Chúng là hai ngôn ngữ khác nhau có cùng một cú pháp.

Tôi thường diễn đạt 'độ đúng của const' trong C # bằng cách xác định chế độ xem chỉ đọc của một lớp:

public interface IReadOnlyCustomer
{
    String Name { get; }
    int Age { get; }
}

public class Customer : IReadOnlyCustomer
{
    private string m_name;
    private int m_age;

    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }

    public int Age
    {
        get { return m_age; }
        set { m_age = value; }
    }
}

12
Chắc chắn rồi, nhưng điều gì sẽ xảy ra nếu một trong các trường của bạn là Danh sách hoặc kiểu nhiều định dạng. Giải pháp của bạn trở nên phức tạp rất nhanh.
Matt Cruikshank, 17/10/08

12
@Trap: đó là toàn bộ điểm của câu hỏi. Trả lại một tập hợp nội bộ là một thực tiễn không tốt vì thế giới bên ngoài có thể sửa đổi nội bộ của bạn, trong C ++, điều này được giải quyết bằng cách sử dụng const: bạn cung cấp chế độ xem liên tục về tập hợp (không thể thêm / xóa các phần tử, chỉ cung cấp một chế độ xem không đổi của nó ) và như vậy nó là an toàn (và một thành ngữ phổ biến). Không cần phải tìm ra giao diện hoặc các thủ thuật khác để tránh mã bên ngoài thay đổi nội bộ của bạn.
David Rodríguez - dribeas

9
@Trap, tôi đồng ý rằng cơ chế này là một trợ giúp cho sự phát triển, bao gồm việc phát hiện lỗi từ các nhà phát triển, có kinh nghiệm hay không. Tôi không đồng ý rằng phải viết giao diện chỉ đọc là một giải pháp tốt hơn theo một số lý do. Điểm đầu tiên là bất cứ khi nào bạn viết một lớp trong C ++, bạn đang định nghĩa một cách ngầm định giao diện không đổi: tập hợp con của các thành viên được khai báo const, mà không phải trả thêm chi phí xác định một giao diện riêng và yêu cầu điều phối thời gian chạy. Đó là, ngôn ngữ cung cấp một cách đơn giản để thực hiện các giao diện hằng thời gian biên dịch.
David Rodríguez - dribeas

10
Điều quan trọng cần lưu ý là const-ness một phần của thiết kế và nó tuyên bố ý định rõ ràng giống như việc ghi một giao diện const bên ngoài. Ngoài ra, việc cung cấp các giao diện const có thể là một nhiệm vụ phức tạp nếu nó phải được xử lý thủ công và có thể yêu cầu ghi gần như nhiều giao diện như các lớp có trong kiểu phức hợp. Điều đó đưa chúng tôi đến điểm cuối cùng của bạn: 'trục trặc mọi thứ do quên thêm const'. Việc thêm các giao diện constở khắp mọi nơi (chi phí phát triển nhỏ) sẽ dễ dàng hơn là viết các giao diện chỉ đọc cho mọi lớp.
David Rodríguez - dribeas

9
Một trong những lý do tôi thực sự thích C # khi nó ra mắt là nó không loại bỏ tất cả các tính năng thực dụng của C ++ như Java đã làm. Java đã thử làm mọi thứ với các giao diện, và đó là một thảm họa. Bằng chứng là trong pudding. Theo thời gian, nhiều tính năng đã được thêm "trở lại" vào Java mà ban đầu bị tố cáo là dị giáo. Thiết kế dựa trên giao diện có vị trí của nó, nhưng khi thực hiện quá mức có thể làm phức tạp một cách không cần thiết kiến ​​trúc của chương trình. Bạn sẽ mất nhiều thời gian hơn để viết mã soạn sẵn và ít thời gian hơn để hoàn thành những việc hữu ích.
kgriffs

29

Để có được lợi ích của const-craziness (hoặc thuần túy trong thuật ngữ lập trình hàm), bạn sẽ cần thiết kế các lớp của mình theo cách để chúng là bất biến, giống như lớp String của c #.

Cách tiếp cận này tốt hơn là chỉ đánh dấu một đối tượng là chỉ đọc, vì với các lớp bất biến, bạn có thể truyền dữ liệu xung quanh dễ dàng trong môi trường đa tác vụ.


8
nhưng tính bất biến không thực sự mở rộng đến các đối tượng phức tạp, hay phải không?
10pn 22/09/08

8
Tôi lập luận rằng nếu đối tượng của bạn phức tạp đến mức không thể thay đổi được, thì bạn sẽ có một ứng cử viên tốt để tái cấu trúc.
Jim Burger

2
Tôi nghĩ cái này là cái hay nhất của nhóm. Các đối tượng bất biến được sử dụng quá thường xuyên.

3
Không chỉ vậy, có những trường hợp bạn muốn có các đối tượng thay đổi (đối tượng thay đổi!) Nhưng vẫn cung cấp chế độ xem chỉ đọc trong hầu hết các trường hợp. Các đối tượng bất biến ngụ ý rằng bất cứ khi nào bạn cần thực hiện một thay đổi (thay đổi xảy ra), bạn sẽ cần tạo một đối tượng mới với tất cả cùng một dữ liệu bên cạnh thay đổi. Hãy xem xét một trường học có các phòng học, học sinh ... bạn có muốn tạo một trường học mới mỗi khi ngày sinh của học sinh trôi qua và tuổi của học sinh đó thay đổi không? Hay có thể chỉ thay đổi độ tuổi ở cấp học sinh, sinh viên cấp phòng, có thể cấp phòng ở cấp trường?
David Rodríguez - dribeas

8
@tenpn: Tính bất biến thực sự có quy mô cực kỳ tốt, khi được thực hiện đúng. Một điều thực sự hữu ích là việc sử dụng các Builderlớp cho các kiểu bất biến lớn (Java và .NET định nghĩa StringBuilderlớp chỉ là một ví dụ).
Konrad Rudolph

24

Tôi chỉ muốn lưu ý với bạn rằng nhiều vùng chứa System.Collections.Generics có phương thức AsReadOnly sẽ cung cấp cho bạn một bộ sưu tập bất biến.


4
Điều này vẫn có vấn đề là các Ts có thể thay đổi được ngay cả khi ReadOnlyCollection <T> không.
arynaq

4

C # không có tính năng như vậy. Bạn có thể chuyển đối số theo giá trị hoặc bằng tham chiếu. Tham khảo chính nó là bất biến trừ khi bạn chỉ định ref modifier. Nhưng dữ liệu được tham chiếu không phải là bất biến. Vì vậy, bạn cần phải cẩn thận nếu muốn tránh các tác dụng phụ.

MSDN:

Thông số vượt qua


Tôi đoán đó là những gì câu hỏi đặt ra sau đó: cách tốt nhất để tránh các tác dụng phụ của việc không có cấu trúc const?
10pn 22/09/08

Thật không may, chỉ có các loại bất biến mới có thể giúp được điều đó. Bạn có thể xem Spec # - có một số kiểm tra thời gian biên dịch thú vị.
aku 22/09/08

3

Giao diện là câu trả lời và thực sự mạnh hơn "const" trong C ++. const là một giải pháp phù hợp với tất cả các vấn đề trong đó "const" được định nghĩa là "không thiết lập thành viên hoặc gọi một cái gì đó đặt thành viên". Đó là một cách viết tắt tốt cho const-ness trong nhiều trường hợp, nhưng không phải tất cả chúng. Ví dụ: hãy xem xét một hàm tính toán giá trị dựa trên một số thành viên nhưng cũng lưu kết quả vào bộ nhớ cache. Trong C ++, nó được coi là không phải const, mặc dù từ quan điểm của người dùng nó về cơ bản là const.

Các giao diện giúp bạn linh hoạt hơn trong việc xác định tập hợp con các khả năng cụ thể mà bạn muốn cung cấp từ lớp của mình. Muốn const-ness? Chỉ cần cung cấp một giao diện không có phương thức đột biến. Bạn muốn cho phép thiết lập một số thứ nhưng không cho phép những thứ khác? Cung cấp một giao diện chỉ với những phương pháp đó.


15
Không đúng 100%. Các phương thức const trong C ++ được phép thay đổi các thành viên được đánh dấu là mutable.
Constantin

3
Đủ công bằng. Và const-casting cho phép bạn loại bỏ const-ness. Cả hai loại đều ngụ ý rằng ngay cả các nhà thiết kế C ++ cũng nhận ra rằng một kích thước phù hợp với tất cả thực sự là một kích thước phù hợp nhất.
munificent 25/09/08

8
Ưu điểm của C ++ là bạn có const cho hầu hết các trường hợp và bạn có thể triển khai các giao diện cho những người khác. Bây giờ, bên cạnh const, C ++ có từ khóa có thể thay đổi để áp dụng cho các thuộc tính dưới dạng bộ nhớ đệm dữ liệu hoặc cơ chế khóa (mutexes hoặc tương tự). Const is not 'sẽ không thay đổi bất kỳ thuộc tính bên trong nào) nhưng đúng hơn là sẽ không thay đổi trạng thái đối tượng như nhận thức từ bên ngoài. Có nghĩa là, bất kỳ cách sử dụng đối tượng nào trước và sau khi gọi một phương thức const sẽ mang lại cùng một kết quả.
David Rodríguez - dribeas

9
loại bỏ const để đột biến comething trong C ++ là hành vi undefiend (quỷ mũi). const_cast dành cho giao tiếp với mã kế thừa mà mặc dù về mặt logic, const không được đánh dấu là const.
jk.

3

Đồng ý với một số người khác về cách sử dụng các trường chỉ đọc mà bạn khởi tạo trong phương thức khởi tạo, để tạo các đối tượng bất biến.

    public class Customer
    {
    private readonly string m_name;
    private readonly int m_age;

    public Customer(string name, int age)
    {
        m_name = name;
        m_age = age;
    }

    public string Name
    {
        get { return m_name; }
    }

    public int Age
    {
        get { return m_age; }
    }
  }

Ngoài ra, bạn cũng có thể thêm phạm vi truy cập vào các thuộc tính, tức là tập hợp nhận và bảo vệ công khai?

    public class Customer
    {
    private string m_name;
    private int m_age;

    protected Customer() 
    {}

    public Customer(string name, int age)
    {
        m_name = name;
        m_age = age;
    }

    public string Name
    {
        get { return m_name; }
        protected set { m_name = value; }
    }

    public int Age
    {
        get { return m_age; }
        protected set { m_age = value; }
    }
  }

3
Đây là những cách tiếp cận khác nhau không thực sự phù hợp với toàn bộ vấn đề. Với mức kiểm soát truy cập, bạn chỉ cho phép các lớp dẫn xuất từ ​​các thay đổi, trong nhiều trường hợp, điều này sẽ không mô hình hóa thế giới thực một cách thích hợp. Một giáo viên không xuất phát từ hồ sơ học sinh mà có thể muốn thay đổi điểm học sinh, học sinh tuy không thể thay đổi điểm nhưng có thể đọc được… chỉ để nêu một ví dụ đơn giản.
David Rodríguez - dribeas

2
  • Các const từ khóa có thể được sử dụng cho các hằng số thời gian biên dịch như các kiểu dữ liệu và chuỗi
  • Các readonly từ khóa có thể được sử dụng cho các hằng số thời gian chạy như các loại tài liệu tham khảo

Vấn đề với chỉ đọc là nó chỉ cho phép tham chiếu (con trỏ) là hằng số. Điều được tham chiếu (trỏ tới) vẫn có thể được sửa đổi. Đây là một phần khó khăn nhưng không có cách nào xung quanh nó. Để triển khai các đối tượng không đổi có nghĩa là làm cho chúng không hiển thị bất kỳ phương thức hoặc thuộc tính có thể thay đổi nào nhưng điều này thật khó xử.

Xem thêm C #: 50 Cách Cụ thể Hiệu quả để Cải thiện C # của bạn (Mục 2 - Chỉ thích đọc thành const.)

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.