Các lớp bên trong riêng tư trong C # - tại sao chúng không được sử dụng thường xuyên hơn?


76

Tôi tương đối mới với C # và mỗi lần tôi bắt đầu làm việc trên một dự án C # (tôi chỉ làm việc với các dự án gần như trưởng thành trong C #) Tôi tự hỏi tại sao không có lớp bên trong?

Có lẽ tôi không hiểu mục tiêu của họ. Đối với tôi, các lớp bên trong - ít nhất là các lớp bên trong riêng tư - trông rất giống "thủ tục bên trong" trong Pascal / Modula-2 / Ada: chúng cho phép chia nhỏ một lớp chính thành các phần nhỏ hơn để dễ hiểu.

Ví dụ: đây là những gì thường thấy:

public class ClassA
{
   public MethodA()
   {
      <some code>
      myObjectClassB.DoSomething(); // ClassB is only used by ClassA
      <some code>
   }
}

public class ClassB
{
   public DoSomething()
   {
   }
}

Vì ClassB sẽ chỉ được sử dụng (ít nhất là trong một thời gian) bởi ClassA, tôi đoán rằng mã này sẽ được diễn đạt tốt hơn như sau:

   public class ClassA
   {
      public MethodA()
      {
         <some code>
         myObjectClassB.DoSomething(); // Class B is only usable by ClassA
         <some code>
      }

      private class ClassB
      {
         public DoSomething()
         {
         }
      }
   }

Tôi rất vui khi nghe ý kiến ​​của bạn về chủ đề này - Tôi nói đúng chứ?


1
Cũng lưu ý rằng bạn không thể có phương pháp mở rộng trong các lớp lồng nhau ..
nawfal

Câu trả lời:


80

Các lớp lồng nhau (có lẽ tốt nhất là tránh từ "bên trong" vì các lớp lồng nhau trong C # hơi khác với các lớp bên trong trong Java) thực sự có thể rất hữu ích.

Một mẫu chưa được đề cập đến là mẫu "enum tốt hơn" - có thể linh hoạt hơn mẫu trong Java:

public abstract class MyCleverEnum
{
    public static readonly MyCleverEnum First = new FirstCleverEnum();
    public static readonly MyCleverEnum Second = new SecondCleverEnum();

    // Can only be called by this type *and nested types*
    private MyCleverEnum()
    {
    }

    public abstract void SomeMethod();
    public abstract void AnotherMethod();

    private class FirstCleverEnum : MyCleverEnum
    {
        public override void SomeMethod()
        {
             // First-specific behaviour here
        }

        public override void AnotherMethod()
        {
             // First-specific behaviour here
        }
    }

    private class SecondCleverEnum : MyCleverEnum
    {
        public override void SomeMethod()
        {
             // Second-specific behaviour here
        }

        public override void AnotherMethod()
        {
             // Second-specific behaviour here
        }
    }
}

Chúng tôi có thể làm với một số hỗ trợ ngôn ngữ để thực hiện một số điều này tự động - và có rất nhiều tùy chọn tôi chưa hiển thị ở đây, chẳng hạn như không thực sự sử dụng một lớp lồng nhau cho tất cả các giá trị hoặc sử dụng cùng một lớp lồng nhau cho nhiều giá trị, nhưng cung cấp cho chúng các tham số xây dựng khác nhau. Nhưng về cơ bản, thực tế là lớp lồng nhau có thể gọi hàm tạo riêng mang lại rất nhiều sức mạnh.


12
Rất thông minh! Nhưng tôi sẽ không bao giờ sử dụng nó. Những người viết sách phần mềm và độc giả của họ (!) Thường là những người thông minh. Họ nghĩ đến rất nhiều điều tươi sáng và điều đó thật tuyệt. Nhưng những người bảo trì phần mềm là những người bình thường không đọc Gamma / Skeet. Khách của tôi: càng có nhiều nó là thông minh, ít nó là maintenable ...
Sylvain Rodrigue

29
Điều đó đúng cho đến khi nó trở thành một mẫu được công nhận. Nó giống như "while ((line = streamReader.ReadLine ())! = Null)" khi bắt đầu có vẻ tệ - tác dụng phụ trong một thời gian! Nhưng khi nó mang tính thành ngữ, bạn sẽ vượt qua mùi đó và đánh giá cao sự ngắn gọn.
Jon Skeet

2
@Jon, cảm ơn! Tôi nghĩ rằng tôi đã hiểu nó ngay bây giờ. MyCleverEnumđóng vai trò là chủ sở hữu của các thành viên enum và giao diện của từng thành viên enum, vì vậy nó ngắn gọn hơn các giải pháp khác. Việc sử dụng các lớp nhúng đảm bảo rằng các lớp hỗ trợ được ẩn khỏi bên ngoài lớp. Hàm khởi tạo riêng làm cho lớp hoạt động giống như một lớp static / singleton từ bên ngoài lớp nhưng giống như một lớp cơ sở trừu tượng từ bên trong lớp.
devuxer

1
Một điều mà tôi thấy khó hiểu khi sử dụng phạm vi lớp như vậy là các lớp bên trong công khai cuối cùng có tên công khai "chấm". Có cách nào sạch để có một lớp truy cập công khai có phạm vi tương tự như một lớp lồng nhau, nhưng có thể truy cập công khai qua một "tên đơn giản'?
supercat

1
@supercat, Bạn có thể đặt chỉ thị using ở đầu tệp using FirstCleverEnum = MyCleverEnum.FirstCleverEnum. Điều này có thể giảm thiểu sự cố, nhưng không có cách nào để "xuất" bí danh này.
Marty Neal

31

Các Hướng dẫn thiết kế khung có các quy tắc tốt nhất cho việc sử dụng các lớp lồng nhau mà tôi đã tìm thấy cho đến nay.

Đây là danh sách tóm tắt ngắn gọn:

  1. Sử dụng kiểu lồng nhau khi mối quan hệ giữa kiểu và kiểu lồng nhau là ngữ nghĩa khả năng truy cập thành viên được mong muốn.

  2. Đừng KHÔNG sử dụng các loại lồng nào như một cấu trúc nhóm logic

  3. Tránh sử dụng các kiểu lồng nhau tiếp xúc công khai.

  4. Đừng KHÔNG sử dụng các loại lồng nhau nếu loại có khả năng được tham chiếu bên ngoài của các loại chứa.

  5. Đừng KHÔNG sử dụng các loại lồng nhau nếu họ cần phải được khởi tạo bởi mã khách hàng.

  6. Đừng KHÔNG định nghĩa một kiểu lồng nhau như một thành viên của một giao diện.


1
một số trong số này khá rõ ràng (chẳng hạn như 4 và 5). Nhưng bạn có thể chỉ ra lý do tại sao các quy tắc khác được khuyến nghị không?
Peter Bagnall

@PeterBagnall là chỉ tôi, hay thứ 6 mâu thuẫn với câu trả lời được chấp nhận?
Forest_mole

@jungle_mole kiểu lồng nhau triển khai giao diện công khai là tốt và phổ biến. Loại kiểu lồng nhau không bao giờ được hiển thị công khai.
Erick

@Erick vâng. tôi vừa hoán đổi hai phần của hai "thiết kế" trong đầu. lý do tbh cho thứ 6 này là rõ ràng. những gì tôi thậm chí đang nghĩ
rừng_mole

12

Bạn nên giới hạn trách nhiệm của từng lớp để mỗi lớp luôn đơn giản, có thể kiểm tra và tái sử dụng . Các lớp bên trong tư nhân đi ngược lại điều đó. Chúng góp phần vào sự phức tạp của lớp bên ngoài, chúng không thể kiểm tra được và chúng không thể tái sử dụng.


Đơn giản, có thể kiểm tra và tái sử dụng là rất tuyệt vời. Nhưng những gì về đóng gói? Khi điều gì đó không nên được nhìn thấy bên ngoài, tại sao lại công khai nó? Trong cuốn sách của tôi, các lớp bên trong có thể làm cho mọi thứ đơn giản hơn một chút. Và chúng là những thứ có thể kiểm tra được (với sự liên kết)!
Sylvain Rodrigue

4
Tôi sẽ chọn tính đóng gói như một đối số chống lại các lớp bên trong. Các lớp bên trong có thể truy cập các thành viên riêng của lớp bên ngoài của chúng!
Wim Coenen

Quyền của bạn ! Có một lớp bên trong thêm một số loại khớp nối giữa nó và lớp bên ngoài - tôi không nhìn thấy nó lúc đầu - xin lỗi. Và cảm ơn !
Sylvain Rodrigue

9
Tôi thấy các lập trình viên luôn sử dụng các cấu trúc dữ liệu như bộ giá trị thay vì các lớp riêng tư. Tôi chắc chắn sẽ tranh luận rằng một lớp riêng dẫn đến mã dễ đọc hơn và ít bị lỗi hơn. Lập luận rằng nó vi phạm tính đóng gói là không đúng. Không có gì về một lớp riêng vi phạm tính đóng gói. Nó được chứa bên trong một lớp và nên và có tất cả các quyền của bất kỳ thành viên nào khác của lớp bên ngoài.
Đánh dấu

Như với tất cả mọi thứ, nó phụ thuộc. Và trong trường hợp này, tôi nghĩ rằng nó phụ thuộc vào các chi tiết cụ thể của lớp lồng nhau. Ví dụ: nếu bạn tạo một dto trong một lớp cha (ví dụ: để giữ sạch chữ ký của phương thức), thì không có gì để kiểm tra. Nó không khác gì việc tạo imo Tuples riêng tư, chỉ rõ ràng hơn. Nhưng nếu bạn tạo một lớp lồng nhau phong phú có chức năng, thì bạn sẽ gặp khó khăn.
Sinaesthetic,

3

Đối với cá nhân tôi, tôi chỉ tạo các lớp private bên trong nếu tôi cần tạo các bộ sưu tập trong quá trình của một đối tượng có thể yêu cầu các phương thức trên chúng.

Nếu không, nó có thể gây ra sự nhầm lẫn cho các nhà phát triển khác đang làm việc trong dự án để thực sự tìm thấy các lớp này, vì họ không rõ lắm về vị trí của chúng.


1
Nhưng vì chúng là các lớp riêng tư, các nhà phát triển khác không nên biết về chúng, tất nhiên, trừ khi họ phải duy trì lớp bên ngoài. Tất nhiên, đây có thể là một vấn đề mà mọi người không nhận thức được các lớp bên trong riêng tư. Cảm ơn câu trả lời của bạn !
Sylvain Rodrigue

16
Nếu điều đó làm cho các nhà phát triển của tôi bối rối, tôi sẽ có được các nhà phát triển mới.
DancesWithBamboo
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.