Tại sao / khi nào bạn nên sử dụng các lớp lồng nhau trong .net? Hay không nên bạn?


93

Trong bài đăng trên blog năm 2008 của Kathleen Dollard , cô ấy trình bày một lý do thú vị để sử dụng các lớp lồng nhau trong .net. Tuy nhiên, cô ấy cũng đề cập rằng FxCop không thích các lớp lồng nhau. Tôi giả định rằng những người viết quy tắc FxCop không ngu ngốc, vì vậy chắc chắn phải có lý do đằng sau vị trí đó, nhưng tôi không thể tìm ra nó.


Wayback Archive liên kết đến bài viết trên blog: web.archive.org/web/20141127115939/https://blogs.msmvps.com/...
iokevins

Như nawfal đã chỉ ra , Eric Lippert , người bạn của chúng tôi đã trả lời một bản sao của câu hỏi này ở đây , câu trả lời bắt đầu bằng câu hỏi, " Sử dụng các lớp lồng nhau khi bạn cần một lớp trợ giúp vô nghĩa bên ngoài lớp; đặc biệt khi lớp lồng nhau có thể sử dụng Các chi tiết triển khai private của lớp bên ngoài .¶ Lập luận của bạn rằng các lớp lồng nhau là vô dụng cũng là một lập luận cho rằng các phương thức private là vô dụng ... "
ruffin

Câu trả lời:


96

Sử dụng một lớp lồng nhau khi lớp bạn đang lồng chỉ hữu ích cho lớp bao quanh. Ví dụ: các lớp lồng nhau cho phép bạn viết một cái gì đó như (đơn giản hóa):

public class SortedMap {
    private class TreeNode {
        TreeNode left;
        TreeNode right;
    }
}

Bạn có thể tạo định nghĩa hoàn chỉnh về lớp của mình ở một nơi, bạn không cần phải chuyển qua bất kỳ vòng PIMPL nào để xác định cách lớp của bạn hoạt động và thế giới bên ngoài không cần xem bất kỳ hoạt động triển khai nào của bạn.

Nếu lớp TreeNode là bên ngoài, bạn sẽ phải tạo tất cả các trường publichoặc tạo một loạt các get/setphương thức để sử dụng nó. Thế giới bên ngoài sẽ có một tầng lớp khác làm ô nhiễm nội tâm của họ.


44
Để thêm vào điều này: Bạn cũng có thể sử dụng các lớp một phần trong các tệp riêng biệt để quản lý mã của bạn tốt hơn một chút. Đặt lớp bên trong vào một tệp riêng biệt (trong trường hợp này là SortedMap.TreeNode.cs). Điều này sẽ giữ cho mã của bạn sạch sẽ, đồng thời giữ cho mã của bạn được tách biệt :)
Erik van Brakel

1
Sẽ có những trường hợp bạn cần đặt lớp lồng nhau là công khai hoặc nội bộ nếu nó đang được sử dụng trong loại trả lại của API công khai hoặc thuộc tính công khai của lớp vùng chứa. Tôi không chắc liệu đó có phải là một thực hành tốt hay không. Những trường hợp như vậy có thể hợp lý hơn khi kéo lớp lồng nhau ra bên ngoài lớp vùng chứa. Lớp System.Windows.Forms.ListViewItem.ListViewSubItem trong .Net framework là một trong những ví dụ như vậy.
RBT

16

Từ Hướng dẫn Java của Sun:

Tại sao lại sử dụng các lớp lồng nhau? Có một số lý do thuyết phục để sử dụng các lớp lồng nhau, trong số đó:

  • Đó là một cách nhóm các lớp một cách hợp lý chỉ được sử dụng ở một nơi.
  • Nó làm tăng khả năng đóng gói.
  • Các lớp lồng nhau có thể dẫn đến mã dễ đọc và dễ bảo trì hơn.

Nhóm các lớp một cách hợp lý — Nếu một lớp chỉ hữu ích cho một lớp khác, thì sẽ hợp lý khi nhúng nó vào lớp đó và giữ hai lớp lại với nhau. Việc lồng các "lớp trợ giúp" như vậy làm cho gói của chúng được sắp xếp hợp lý hơn.

Tăng tính đóng gói — Hãy xem xét hai lớp cấp cao nhất, A và B, trong đó B cần quyền truy cập vào các thành viên của A mà nếu không sẽ được khai báo là riêng tư. Bằng cách ẩn lớp B trong lớp A, các thành viên của A có thể được khai báo là riêng tư và B có thể truy cập chúng. Ngoài ra, bản thân B có thể bị che giấu khỏi thế giới bên ngoài. <- Điều này không áp dụng cho việc triển khai các lớp lồng nhau của C #, điều này chỉ áp dụng cho Java.

Mã dễ đọc hơn, dễ bảo trì hơn — Việc lồng các lớp nhỏ trong các lớp cấp cao nhất sẽ đặt mã gần hơn với nơi nó được sử dụng.


1
Điều này không thực sự áp dụng, vì bạn không thể truy cập các biến cá thể từ lớp bao quanh trong C # như bạn có thể làm trong Java. Chỉ các thành viên tĩnh mới có thể truy cập được.
Ben Baron

5
Tuy nhiên, nếu bạn chuyển một thể hiện của lớp bao quanh vào lớp lồng nhau, thì lớp lồng nhau có toàn quyền truy cập vào tất cả các thành viên thông qua biến thể hiện đó ... vì vậy thực sự, nó giống như Java tạo ra biến thể hiện là ẩn, trong khi trong C #, bạn phải làm cho nó rõ ràng.
Alex

@Alex Không, không phải vậy, trong Java, lớp lồng nhau thực sự nắm bắt cá thể lớp cha khi được khởi tạo - trong số những thứ khác, điều này có nghĩa là nó ngăn cha mẹ bị thu gom rác. Điều đó cũng có nghĩa là không thể khởi tạo lớp lồng nhau nếu không có lớp cha. Vì vậy, không, chúng không giống nhau ở tất cả.
Tomáš Zato - Phục hồi Monica

2
@ TomášZato Mô tả của tôi khá phù hợp. Thực tế có một biến cá thể cha ngầm trong các lớp lồng nhau trong Java, trong khi trong C #, bạn phải đưa cá thể lớp bên trong một cách rõ ràng. Một hệ quả của điều này, như bạn nói, là các lớp bên trong của Java phải có một cá thể cha, trong khi C # thì không. Điểm chính của tôi trong mọi trường hợp là các lớp bên trong của C # cũng có thể truy cập vào các trường và thuộc tính riêng của cha mẹ nó, nhưng nó phải được thông qua cá thể cha một cách rõ ràng để có thể thực hiện điều đó.
Alex,

9

Mô hình singleton hoàn toàn lười biếng và an toàn cho sợi chỉ

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

nguồn: http://www.yoda.arachsys.com/csharp/singleton.html


5

Nó phụ thuộc vào cách sử dụng. Tôi hiếm khi sử dụng một lớp lồng nhau Công khai mà luôn sử dụng các lớp lồng nhau Riêng tư. Một lớp lồng nhau riêng tư có thể được sử dụng cho một đối tượng con chỉ được sử dụng bên trong lớp cha. Một ví dụ về điều này sẽ là nếu một lớp HashTable chứa một đối tượng Entry riêng để chỉ lưu trữ dữ liệu trong nội bộ.

Nếu lớp được sử dụng bởi người gọi (bên ngoài), tôi thường thích làm cho nó trở thành một lớp độc lập riêng biệt.


5

Ngoài những lý do khác được liệt kê ở trên, có một lý do nữa mà tôi có thể nghĩ đến không chỉ để sử dụng các lớp lồng nhau, mà trên thực tế là các lớp lồng nhau công khai. Đối với những người làm việc với nhiều lớp chung có cùng tham số kiểu chung, khả năng khai báo một không gian tên chung sẽ cực kỳ hữu ích. Thật không may, .Net (hoặc ít nhất là C #) không hỗ trợ ý tưởng về không gian tên chung. Vì vậy, để thực hiện cùng một mục tiêu, chúng ta có thể sử dụng các lớp chung để thực hiện cùng một mục tiêu. Lấy các lớp ví dụ sau liên quan đến một thực thể logic:

public  class       BaseDataObject
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  class       BaseDataObjectList
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
:   
                    CollectionBase<tDataObject>
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseBusiness
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseDataAccess
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

Chúng ta có thể đơn giản hóa chữ ký của các lớp này bằng cách sử dụng một không gian tên chung (được triển khai thông qua các lớp lồng nhau):

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{

    public  class       BaseDataObject {}

    public  class       BaseDataObjectList : CollectionBase<tDataObject> {}

    public  interface   IBaseBusiness {}

    public  interface   IBaseDataAccess {}

}

Sau đó, thông qua việc sử dụng các lớp từng phần như được đề xuất bởi Erik van Brakel trong một nhận xét trước đó, bạn có thể tách các lớp thành các tệp lồng nhau riêng biệt. Tôi khuyên bạn nên sử dụng tiện ích mở rộng Visual Studio như NestIn để hỗ trợ lồng các tệp lớp một phần. Điều này cho phép các tệp lớp "không gian tên" cũng được sử dụng để tổ chức các tệp lớp lồng nhau trong một thư mục theo cách tương tự.

Ví dụ:

Entity.cs

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{
}

Entity.BaseDataObject.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObject
    {

        public  DataTimeOffset  CreatedDateTime     { get; set; }
        public  Guid            CreatedById         { get; set; }
        public  Guid            Id                  { get; set; }
        public  DataTimeOffset  LastUpdateDateTime  { get; set; }
        public  Guid            LastUpdatedById     { get; set; }

        public
        static
        implicit    operator    tDataObjectList(DataObject dataObject)
        {
            var returnList  = new tDataObjectList();
            returnList.Add((tDataObject) this);
            return returnList;
        }

    }

}

Entity.BaseDataObjectList.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObjectList : CollectionBase<tDataObject>
    {

        public  tDataObjectList ShallowClone() 
        {
            var returnList  = new tDataObjectList();
            returnList.AddRange(this);
            return returnList;
        }

    }

}

Entity.IBaseBusiness.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseBusiness
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

Entity.IBaseDataAccess.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseDataAccess
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

Các tệp trong trình khám phá giải pháp studio trực quan sau đó sẽ được sắp xếp như vậy:

Entity.cs
+   Entity.BaseDataObject.cs
+   Entity.BaseDataObjectList.cs
+   Entity.IBaseBusiness.cs
+   Entity.IBaseDataAccess.cs

Và bạn sẽ triển khai không gian tên chung như sau:

User.cs

public
partial class   User
:
                Entity
                <
                    User.DataObject, 
                    User.DataObjectList, 
                    User.IBusiness, 
                    User.IDataAccess
                >
{
}

User.DataObject.cs

partial class   User
{

    public  class   DataObject : BaseDataObject 
    {
        public  string  UserName            { get; set; }
        public  byte[]  PasswordHash        { get; set; }
        public  bool    AccountIsEnabled    { get; set; }
    }

}

User.DataObjectList.cs

partial class   User
{

    public  class   DataObjectList : BaseDataObjectList {}

}

User.IBusiness.cs

partial class   User
{

    public  interface   IBusiness : IBaseBusiness {}

}

User.IDataAccess.cs

partial class   User
{

    public  interface   IDataAccess : IBaseDataAccess {}

}

Và các tệp sẽ được sắp xếp trong trình khám phá giải pháp như sau:

User.cs
+   User.DataObject.cs
+   User.DataObjectList.cs
+   User.IBusiness.cs
+   User.IDataAccess.cs

Trên đây là một ví dụ đơn giản về việc sử dụng lớp ngoài làm không gian tên chung. Trước đây, tôi đã xây dựng "không gian tên chung" chứa 9 tham số kiểu trở lên. Việc phải giữ cho các tham số kiểu đó được đồng bộ hóa trên chín kiểu mà tất cả những gì cần thiết để biết các tham số kiểu thật tẻ nhạt, đặc biệt là khi thêm một tham số mới. Việc sử dụng các không gian tên chung làm cho mã đó dễ quản lý và dễ đọc hơn.


3

Nếu tôi hiểu đúng bài viết của Katheleen, cô ấy đề xuất sử dụng lớp lồng nhau để có thể viết SomeEntity.Collection thay vì EntityCollection <SomeEntity>. Theo ý kiến ​​của tôi, đó là cách gây tranh cãi để giúp bạn tiết kiệm một số công việc đánh máy. Tôi khá chắc rằng trong các bộ sưu tập ứng dụng trong thế giới thực sẽ có một số khác biệt trong việc triển khai, vì vậy dù sao thì bạn cũng cần phải tạo lớp riêng biệt. Tôi nghĩ rằng việc sử dụng tên lớp để giới hạn phạm vi lớp khác không phải là một ý kiến ​​hay. Nó gây ô nhiễm intellisense và củng cố sự phụ thuộc giữa các lớp. Sử dụng không gian tên là một cách tiêu chuẩn để kiểm soát phạm vi lớp. Tuy nhiên, tôi thấy rằng việc sử dụng các lớp lồng nhau như trong bình luận @hazzen là có thể chấp nhận được trừ khi bạn có hàng tấn các lớp lồng nhau đó là dấu hiệu của thiết kế xấu.


1

Tôi thường sử dụng các lớp lồng nhau để ẩn chi tiết triển khai. Một ví dụ từ câu trả lời của Eric Lippert ở đây:

abstract public class BankAccount
{
    private BankAccount() { }
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount { ... }
    public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
    private sealed class SavingsAccount : BankAccount { ... }
}

Mô hình này thậm chí còn trở nên tốt hơn khi sử dụng generic. Xem câu hỏi này để biết hai ví dụ thú vị. Vì vậy, tôi kết thúc việc viết

Equality<Person>.CreateComparer(p => p.Id);

thay vì

new EqualityComparer<Person, int>(p => p.Id);

Ngoài ra, tôi có thể có một danh sách chung Equality<Person>nhưng khôngEqualityComparer<Person, int>

var l = new List<Equality<Person>> 
        { 
         Equality<Person>.CreateComparer(p => p.Id),
         Equality<Person>.CreateComparer(p => p.Name) 
        }

trong khi

var l = new List<EqualityComparer<Person, ??>>> 
        { 
         new EqualityComparer<Person, int>>(p => p.Id),
         new EqualityComparer<Person, string>>(p => p.Name) 
        }

là không thể. Đó là lợi ích của việc kế thừa lớp lồng nhau từ lớp cha.

Một trường hợp khác (cùng bản chất - ẩn triển khai) là khi bạn muốn làm cho các thành viên của một lớp (trường, thuộc tính, v.v.) chỉ có thể truy cập cho một lớp duy nhất:

public class Outer 
{
   class Inner //private class
   {
       public int Field; //public field
   }

   static inner = new Inner { Field = -1 }; // Field is accessible here, but in no other class
}

1

Một cách sử dụng khác chưa được đề cập cho các lớp lồng nhau là sự phân tách của các kiểu chung. Ví dụ: giả sử một người muốn có một số họ chung của các lớp tĩnh có thể nhận các phương thức với nhiều tham số khác nhau, cùng với các giá trị cho một số tham số đó và tạo ra các đại biểu với ít tham số hơn. Ví dụ, người ta muốn có một phương thức tĩnh có thể nhận một Action<string, int, double>và mang lại một phương thức String<string, int>sẽ gọi hành động được cung cấp truyền 3.5 là double; người ta cũng có thể muốn có một phương thức tĩnh có thể nhận một Action<string, int, double>và mang lại một Action<string>, chuyển 7như là int5.3như là double. Sử dụng các lớp lồng nhau chung, người ta có thể sắp xếp để các lệnh gọi phương thức giống như:

MakeDelegate<string,int>.WithParams<double>(theDelegate, 3.5);
MakeDelegate<string>.WithParams<int,double>(theDelegate, 7, 5.3);

hoặc vì các kiểu sau trong mỗi biểu thức có thể được suy ra mặc dù các kiểu trước không thể:

MakeDelegate<string,int>.WithParams(theDelegate, 3.5);
MakeDelegate<string>.WithParams(theDelegate, 7, 5.3);

Sử dụng các kiểu chung được lồng vào nhau giúp bạn có thể biết được những đại biểu nào có thể áp dụng cho những phần nào của mô tả kiểu tổng thể.


1

Các lớp lồng nhau có thể được sử dụng cho các nhu cầu sau:

  1. Phân loại dữ liệu
  2. Khi logic của lớp chính phức tạp và bạn cảm thấy như bạn yêu cầu các đối tượng cấp dưới để quản lý lớp
  3. Khi bạn rằng trạng thái và sự tồn tại của lớp hoàn toàn phụ thuộc vào lớp bao quanh

0

Như nawfal đã đề cập đến việc triển khai mẫu Abstract Factory, mã đó có thể được điều chỉnh để đạt được mẫu Class Cluster dựa trên mẫu Abstract Factory.


0

Tôi thích lồng ghép các ngoại lệ là duy nhất cho một lớp, tức là. những cái không bao giờ được ném từ bất kỳ nơi nào khác.

Ví dụ:

public class MyClass
{
    void DoStuff()
    {
        if (!someArbitraryCondition)
        {
            // This is the only class from which OhNoException is thrown
            throw new OhNoException(
                "Oh no! Some arbitrary condition was not satisfied!");
        }
        // Do other stuff
    }

    public class OhNoException : Exception
    {
        // Constructors calling base()
    }
}

Điều này giúp giữ cho các tệp dự án của bạn gọn gàng và không chứa đầy hàng trăm lớp ngoại lệ nhỏ bé.


0

Hãy nhớ rằng bạn sẽ cần kiểm tra lớp lồng nhau. Nếu nó là riêng tư, bạn sẽ không thể kiểm tra nó một cách riêng biệt.

Tuy nhiên, bạn có thể làm cho nó bên trong kết hợp với InternalsVisibleTothuộc tính . Tuy nhiên, điều này cũng giống như việc tạo một trường riêng tư chỉ dành cho mục đích thử nghiệm mà tôi cho là tài liệu tự lập không tốt.

Vì vậy, bạn có thể chỉ muốn triển khai các lớp lồng nhau riêng tư có độ phức tạp thấp.


0

có cho trường hợp này:

class Join_Operator
{

    class Departamento
    {
        public int idDepto { get; set; }
        public string nombreDepto { get; set; }
    }

    class Empleado
    {
        public int idDepto { get; set; }
        public string nombreEmpleado { get; set; }
    }

    public void JoinTables()
    {
        List<Departamento> departamentos = new List<Departamento>();
        departamentos.Add(new Departamento { idDepto = 1, nombreDepto = "Arquitectura" });
        departamentos.Add(new Departamento { idDepto = 2, nombreDepto = "Programación" });

        List<Empleado> empleados = new List<Empleado>();
        empleados.Add(new Empleado { idDepto = 1, nombreEmpleado = "John Doe." });
        empleados.Add(new Empleado { idDepto = 2, nombreEmpleado = "Jim Bell" });

        var joinList = (from e in empleados
                        join d in departamentos on
                        e.idDepto equals d.idDepto
                        select new
                        {
                            nombreEmpleado = e.nombreEmpleado,
                            nombreDepto = d.nombreDepto
                        });
        foreach (var dato in joinList)
        {
            Console.WriteLine("{0} es empleado del departamento de {1}", dato.nombreEmpleado, dato.nombreDepto);
        }
    }
}

Tại sao? Thêm một số ngữ cảnh vào mã trong giải pháp của bạn để giúp người đọc trong tương lai hiểu được lý do đằng sau câu trả lời của bạn.
Grant Miller

0

Dựa trên hiểu biết của tôi về khái niệm này, chúng tôi có thể sử dụng tính năng này khi các lớp liên quan đến nhau về mặt khái niệm. Ý tôi là một số trong số chúng hoàn thành một Mục trong doanh nghiệp của chúng tôi giống như các thực thể tồn tại trong thế giới DDD giúp một đối tượng gốc tổng hợp hoàn thành logic nghiệp vụ của nó.

Để làm rõ, tôi sẽ trình bày điều này qua một ví dụ:

Hãy tưởng tượng rằng chúng ta có hai lớp như Order và OrderItem. Trong lớp đơn hàng, chúng tôi sẽ quản lý tất cả các orderItems và trong OrderItem, chúng tôi đang giữ dữ liệu về một đơn hàng để làm rõ, bạn có thể xem các lớp bên dưới:

 class Order
    {
        private List<OrderItem> _orderItems = new List<OrderItem>();

        public void AddOrderItem(OrderItem line)
        {
            _orderItems.Add(line);
        }

        public double OrderTotal()
        {
            double total = 0;
            foreach (OrderItem item in _orderItems)
            {
                total += item.TotalPrice();
            }

            return total;
        }

        // Nested class
        public class OrderItem
        {
            public int ProductId { get; set; }
            public int Quantity { get; set; }
            public double Price { get; set; }
            public double TotalPrice() => Price * Quantity;
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            Order order = new Order();

            Order.OrderItem orderItem1 = new Order.OrderItem();
            orderItem1.ProductId = 1;
            orderItem1.Quantity = 5;
            orderItem1.Price = 1.99;
            order.AddOrderItem(orderItem1);

            Order.OrderItem orderItem2 = new Order.OrderItem();
            orderItem2.ProductId = 2;
            orderItem2.Quantity = 12;
            orderItem2.Price = 0.35;
            order.AddOrderItem(orderItem2);

            Console.WriteLine(order.OrderTotal());
            ReadLine();
        }


    }
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.