2010-05-12 34 views
12

Bối cảnh: Tôi đã kèm theo (cha mẹ) lớp E với lớp lồng nhau N với một số trường hợp N trong E. Trong lớp (cha) kèm theo tôi đang thực hiện một số phép tính và tôi đặt giá trị cho mỗi thể hiện của lớp lồng nhau. Một cái gì đó như thế này:cách truy cập vào các thành viên riêng tư của lớp lồng nhau?

n1.field1 = ...; 
n1.field2 = ...; 
n1.field3 = ...; 
n2.field1 = ...; 
... 

Đây là một phương pháp đánh giá lớn (trong lớp cha). Ý định của tôi là - vì tất cả các phép tính đều ở trong lớp cha (chúng không thể được thực hiện trên mỗi thể hiện lồng nhau vì nó sẽ làm cho mã phức tạp hơn) - làm cho các setters chỉ có sẵn cho lớp cha và getters công khai.

Và bây giờ có một vấn đề:

  • khi tôi làm cho setters tư nhân, tầng lớp phụ huynh không thể acces họ
  • khi tôi làm cho họ công khai, mọi người đều có thể thay đổi các giá trị
  • và C# không có khái niệm bạn bè
  • Tôi không thể chuyển giá trị trong hàm tạo vì cơ chế đánh giá lười được sử dụng (vì vậy các cá thể phải được tạo khi tham chiếu chúng - tôi tạo tất cả đối tượng và phép tính được kích hoạt theo yêu cầu)

Tôi bị kẹt - cách thực hiện điều này (giới hạn quyền truy cập tối đa lớp cha, không còn nữa, không kém)?


tôi nghi ngờ tôi sẽ nhận được câu trả lời-câu hỏi đầu tiên - "nhưng tại sao bạn không chia đánh giá cho mỗi trường" - vì vậy tôi trả lời này bằng ví dụ: làm thế nào để bạn tính toán min và max giá trị của một bộ sưu tập? Một cách nhanh chóng? Câu trả lời là - trong một lần. Đây là lý do tại sao tôi có một hàm eval tính toán và đặt tất cả các trường cùng một lúc.

+0

Từ khóa nội bộ có đủ không? – Kimi

+3

Quá nhiều :-). – greenoldman

+0

Bạn không tin tưởng đồng nghiệp của mình? –

Trả lời

7

Nếu bạn có thể đặt các lớp cha và con trong một hội đồng khác, bạn có thể sử dụng internal cho những người định cư. Đó thường là cách điều này được xử lý trong tự nhiên.

EDIT:

Thomas Levesque's answer đã cho tôi một ý tưởng:

class Program 
{ 
    static void Main(string[] args) 
    { 
     E myE = new E(); 

     Console.WriteLine("E.N1.Field1 = " + myE.N1.Field1); 
     Console.WriteLine("E.N2.Field1 = " + myE.N2.Field1); 
    } 

    public interface IN 
    { 
     int Field1 { get; } 
    } 

    public class E 
    { 
     private N _n1 = new N(); 
     private N _n2 = new N(); 

     public E() 
     { 
      _n1.Field1 = 42; 
      _n2.Field1 = 23; 
     } 

     public IN N1 
     { 
      get { return _n1; } 
     } 

     public IN N2 
     { 
      get { return _n2; } 
     } 

     private class N : IN 
     { 
      private int _field1; 

      public int Field1 
      { 
       get { return _field1; } 
       set { _field1 = value; } 
      } 
     } 
    } 
} 

Tùy thuộc vào cách bạn cần để lộ lớp con N, điều này có thể làm việc.

+1

Cảm ơn bạn, nhưng nó là một tập tin duy nhất, không phải là lớn, do đó, nó có vẻ như một chút overkill thiết lập lắp ráp riêng biệt này. – greenoldman

+0

Cập nhật câu trả lời của tôi dựa trên câu trả lời của Thomas Levesque. – Codesleuth

+0

Làm đẹp (giao diện có thể lồng nhau).CẢM ƠN BẠN! – greenoldman

-2

làm cho các lĩnh vực "bảo vệ nội bộ"

nếu các lớp lồng nhau là tin bạn có thể sử dụng obly "nội bộ" đối với những lĩnh vực.

+0

Sẽ không phải là các lĩnh vực được tiếp xúc với toàn bộ hội? – greenoldman

+0

trong trường hợp "trường nội bộ và lớp riêng": không vì lớp học là riêng tư. trường hợp khác: có. nhưng hội đồng của chính nó phải nằm dưới quyền kiểm soát của bạn. thường đó không phải là một vấn đề. – Jack

+3

Thực ra nó luôn là vấn đề - một năm sau, bạn có thể có cùng ý định tốt nhất, nhưng bạn có thể quên thiết kế. Mã nên phản ánh thiết kế, không phải ý kiến ​​hay mong muốn. – greenoldman

42

Bạn có thể khai báo bên trong E giao diện riêng IN, được triển khai rõ ràng bởi N. Giao diện này sẽ tiếp xúc với các thành viên của N truy cập chỉ bằng cách E:

public class E 
{ 
    public void Foo() 
    { 
     IN n = new N(); 
     n.Field1 = 42; 
    } 

    public class N : IN 
    { 
     private int _field1; 

     int IN.Field1 
     { 
      get { return _field1; } 
      set { _field1 = value; } 
     } 
    } 

    private interface IN 
    { 
     int Field1 { get; set; } 
    } 
} 
+0

Cảm ơn bạn. Giao diện không thể được thực hiện riêng bởi vì tôi sẽ không đọc các giá trị bên ngoài lớp cha (tôi cần getters công khai), và một khi nó được công khai tôi cũng có thể sử dụng setters công khai. – greenoldman

+2

Tôi nghĩ rằng bạn hiểu lầm câu trả lời của tôi ... giao diện 'IN' không bao giờ được dự định là công khai. Nó chỉ là một cách để lớp lồng nhau chỉ hiển thị các thuộc tính cho lớp chứa. Bạn cũng có thể tạo các getters công khai trong 'N' nếu bạn cần chúng –

+0

Nếu giao diện là riêng tư, nó không có sẵn bên ngoài lớp cha. – greenoldman

4

Một lựa chọn khác là để lại các thành viên mà bạn muốn được công tư if (lồng) lớp học của bạn là tư nhân. Nếu các trường của lớp riêng là công khai, nó chỉ được tiếp xúc với lớp kèm theo.

public class E 
{ 
    public void Foo() 
    { 
     IN n = new N(); 
     n.field1 = 42; 
    } 

    class N : IN 
    { 
     public int _field1; 
    } 
} 

Bây giờ N là chỉ hiển thị cho E, vì vậy n._field1 là chỉ các vấn đề công cộng để E, và bạn đang an toàn ..

1

Đây là một câu hỏi cũ, nhưng ở đây đi một giải pháp khả thi điều đó không' t sử dụng giao diện.

Bạn có thể có một chức năng tĩnh trong lớp bên trong đó thiết lập các đại biểu trong các lớp bên ngoài, như vậy:

public class Outer { 
    private delegate void _operateDlg(Inner inner, bool value); 
    private static _operateDlg _validate; 

    static Outer() { 
     Inner.Init(); 
    } 

    public void Set(Inner inner, bool value) { 
     _validate(inner, value); 
    } 

    public class Inner { 
     public bool IsValid {get; private set; } 
     public static void Init() { 
      Outer._validate += delegate(Inner i, bool value) { 
       i.IsValid = value; 
      }; 
     } 
    } 
} 

Bạn có thể đặt tất cả các loại của các đại biểu khác nhau trong các lớp bên ngoài mà bạn chỉ định với Phương thức Inner.Init(), chẳng hạn như các phương thức trả về một thể hiện của lớp Inner thông qua một hàm tạo riêng hoặc các getters/setters của một trường cụ thể.

Nếu bạn không nhớ có hàm Init() tĩnh bổ sung trong lớp bên trong, thì điều này không phải thay đổi. Nhưng nếu bạn không muốn phương pháp Init() để được hiển thị, bạn có thể dùng phản ánh để gọi nó là:

using System.Reflection; 

public class Outer { 
    private delegate void _operateDlg(Inner inner, bool value); 
    private static _operateDlg _validate; 

    static Outer() { 
     typeof(Inner).GetMethod("Init", 
      BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null); 
    } 

    public void Set(Inner inner, bool value) { 
     _validate(inner, value); 
    } 

    public class Inner { 
     public bool IsValid {get; private set; } 
     private static void Init() { 
      Outer._validate = delegate(Inner i, bool value) { 
       i.IsValid = value; 
      }; 
     } 
    } 
} 

Tôi biết rằng người ta có thể sử dụng Reflection để vượt qua giới hạn truy cập tin anyway, nhưng sử dụng nó chỉ để gọi một phương thức Init() duy nhất mà sau đó gán cho các đại biểu thích hợp là một giải pháp sạch hơn và linh hoạt hơn theo ý kiến ​​của tôi. Phương án thay thế sẽ là gọi phản chiếu cho mỗi đại biểu đơn lẻ mà bạn có thể muốn tạo, và thậm chí sau đó có thể có những hạn chế (chẳng hạn như không có khả năng tạo đại biểu cho các nhà xây dựng).

Giải pháp trên không chỉ hỗ trợ các nhà xây dựng gói, mà nó chỉ sử dụng Reflection một lần trong suốt thời gian của một chương trình, vì vậy không nên có một hiệu suất đáng chú ý khác ngoài thực tế là bạn đang sử dụng các đại biểu để đạt được nên được cho phép là truy cập trực tiếp ngay từ đầu. Tôi không biết tại sao C# không hỗ trợ điều này, và tôi không thể nghĩ ra một lý do chính đáng tại sao nó không.

Các vấn đề liên quan