.net에서 중첩 클래스를 사용해야하는 이유 / 언제? 아니면 안 그래?


93

에서 캐슬린 Dollard의 2008 블로그 게시물 , 그녀는 그물에 중첩 된 클래스를 사용하는 흥미로운 이유를 제시한다. 그러나 그녀는 또한 FxCop이 중첩 클래스를 좋아하지 않는다고 언급합니다. 나는 FxCop 규칙을 작성하는 사람들이 어리석지 않다고 가정하고 있으므로 그 위치 뒤에 추론이 있어야하지만 찾을 수 없었습니다.


블로그 게시물에 뒤로 아카이브 링크 : web.archive.org/web/20141127115939/https://blogs.msmvps.com/...은
iokevins

으로 nawfal 점 밖으로 , 우리의 친구 에릭 Lippert의 여기이 질문의 중복 대답 , "로 시작하는 문제의 답 은 의미가 외부의 클래스 헬퍼 클래스를 필요로 할 때 사용 중첩 된 클래스, 중첩 클래스를 사용할 수있다 특히 중첩 된 클래스가 쓸모 있다는 외부 class.¶Your 인수의 개인 구현 세부 사항은 개인 방법은 쓸모가 있다는 주장입니다 ... "
러핀

답변:


96

중첩하는 클래스가 둘러싸는 클래스에만 유용 할 때 중첩 된 클래스를 사용하십시오. 예를 들어, 중첩 클래스를 사용하면 다음과 같이 작성할 수 있습니다.

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

한 곳에서 클래스를 완전히 정의 할 수 있으며, 클래스 작동 방식을 정의하기 위해 PIMPL 후프를 뛰어 넘을 필요가 없으며, 외부 세계는 구현을 볼 필요가 없습니다.

TreeNode 클래스가 외부 클래스 인 경우 모든 필드를 만들거나이 를 사용하기 public위한 여러 get/set메서드를 만들어야 합니다. 외부 세계는 지능을 오염시키는 또 다른 계급을 가질 것입니다.


44
여기에 추가하려면 : 별도의 파일에서 부분 클래스를 사용하여 코드를 좀 더 잘 관리 할 수도 있습니다. 내부 클래스를 별도의 파일 (이 경우 SortedMap.TreeNode.cs)에 넣습니다. 이렇게하면 코드를 깨끗하게 유지하고 코드를 분리하여 유지해야합니다. :)
Erik van Brakel

1
공용 API의 반환 유형 또는 컨테이너 클래스의 공용 속성에서 사용되는 경우 중첩 클래스를 공용 또는 내부로 만들어야하는 경우가 있습니다. 그래도 좋은 습관인지 모르겠습니다. 이러한 경우 컨테이너 클래스 외부에서 중첩 된 클래스를 꺼내는 것이 더 합리적 일 수 있습니다. .Net 프레임 워크의 System.Windows.Forms.ListViewItem.ListViewSubItem 클래스가 그러한 예입니다.
RBT

16

Sun의 Java Tutorial에서 :

중첩 클래스를 사용하는 이유 중첩 클래스를 사용하는 몇 가지 이유는 다음과 같습니다.

  • 한 곳에서만 사용되는 클래스를 논리적으로 그룹화하는 방법입니다.
  • 캡슐화를 증가시킵니다.
  • 중첩 된 클래스는 더 읽기 쉽고 유지 관리 가능한 코드로 이어질 수 있습니다.

논리적 클래스 그룹화 — 클래스가 다른 하나의 클래스에만 유용하면 해당 클래스에 포함하고 두 클래스를 함께 유지하는 것이 논리적입니다. 이러한 "도우미 클래스"를 중첩하면 패키지가 더욱 간소화됩니다.

캡슐화 향상-B는 비공개로 선언 된 A의 멤버에 액세스해야하는 두 개의 최상위 클래스 A와 B를 고려합니다. 클래스 A 내에 클래스 B를 숨기면 A의 멤버를 private으로 선언하고 B가 액세스 할 수 있습니다. 또한 B 자체는 외부 세계에서 숨길 수 있습니다. <-이것은 C #의 중첩 클래스 구현에는 적용되지 않으며 Java에만 적용됩니다.

더 읽기 쉽고 관리하기 쉬운 코드 — 최상위 클래스 내에 작은 클래스를 중첩하면 코드가 사용되는 위치에 더 가깝게 배치됩니다.


1
Java에서와 같이 C #의 포함 클래스에서 인스턴스 변수에 액세스 할 수 없기 때문에 실제로 적용되지 않습니다. 정적 멤버 만 액세스 할 수 있습니다.
Ben Baron

5
그러나 둘러싸는 클래스의 인스턴스를 중첩 된 클래스에 전달하면 중첩 된 클래스는 해당 인스턴스 변수를 통해 모든 멤버에 대한 전체 액세스 권한을 갖게됩니다. 실제로 Java가 인스턴스 변수를 암시 적으로 만드는 반면 C #에서는 명시 적으로 만드십시오.
Alex

@Alex 아니요. Java에서 중첩 된 클래스는 인스턴스화 될 때 실제로 부모 클래스 인스턴스를 캡처합니다. 이는 무엇보다도 부모가 가비지 수집되는 것을 방지한다는 것을 의미합니다. 또한 중첩 된 클래스는 부모 클래스없이 인스턴스화 될 수 없습니다. 아니, 이것들은 전혀 동일하지 않습니다.
Tomáš Zato-Monica 복원

2
@ TomášZato 제 설명은 정말 적절합니다. Java의 중첩 클래스에는 사실상 암시 적 부모 인스턴스 변수가 있지만 C #에서는 내부 클래스에 인스턴스를 명시 적으로 전달해야합니다. 그 결과 Java의 내부 클래스에는 부모 인스턴스가 있어야하지만 C #에는 그렇지 않습니다. 어떤 경우 든 내 요점은 C #의 내부 클래스도 부모 프라이빗 필드와 속성에 액세스 할 수 있지만이를 수행하려면 부모 인스턴스를 명시 적으로 전달해야한다는 것입니다.
알렉스

9

완전히 지연되고 스레드로부터 안전한 싱글 톤 패턴

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();
    }
}

출처 : http://www.yoda.arachsys.com/csharp/singleton.html


5

사용법에 따라 다릅니다. 나는 Public 중첩 클래스를 거의 사용하지 않지만 항상 Private 중첩 클래스를 사용합니다. 전용 중첩 클래스는 부모 내부에서만 사용하도록 의도 된 하위 개체에 사용할 수 있습니다. 이에 대한 예는 HashTable 클래스에 내부적으로 만 데이터를 저장하는 개인 항목 개체가 포함 된 경우입니다.

클래스가 호출자 (외부 적으로)에 의해 사용되도록 의도 된 경우 일반적으로 별도의 독립형 클래스로 만드는 것을 좋아합니다.


5

위에 나열된 다른 이유 외에도 중첩 클래스를 사용할뿐만 아니라 실제로 공용 중첩 클래스를 사용해야하는 이유가 하나 더 있습니다. 동일한 제네릭 형식 매개 변수를 공유하는 여러 제네릭 클래스로 작업하는 사람들에게는 제네릭 네임 스페이스를 선언하는 기능이 매우 유용합니다. 불행히도 .Net (또는 적어도 C #)은 제네릭 네임 스페이스의 개념을 지원하지 않습니다. 따라서 동일한 목표를 달성하기 위해 동일한 목표를 달성하기 위해 일반 클래스를 사용할 수 있습니다. 논리적 엔티티와 관련된 다음 예제 클래스를 사용하십시오.

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

일반 네임 스페이스 (중첩 된 클래스를 통해 구현 됨)를 사용하여 이러한 클래스의 서명을 단순화 할 수 있습니다.

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

}

그런 다음 이전 주석에서 Erik van Brakel이 제안한 부분 클래스를 사용하여 클래스를 별도의 중첩 파일로 분리 할 수 ​​있습니다. 부분 클래스 파일 중첩을 지원하려면 NestIn과 같은 Visual Studio 확장을 사용하는 것이 좋습니다. 이렇게하면 "네임 스페이스"클래스 파일을 사용하여 폴더에 중첩 된 클래스 파일을 구성 할 수도 있습니다.

예를 들면 :

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);
    }

}

Visual Studio 솔루션 탐색기의 파일은 다음과 같이 구성됩니다.

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

그리고 다음과 같은 일반 네임 스페이스를 구현합니다.

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

}

그리고 파일은 솔루션 탐색기에서 다음과 같이 구성됩니다.

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

위는 외부 클래스를 일반 네임 스페이스로 사용하는 간단한 예입니다. 과거에 9 개 이상의 유형 매개 변수를 포함하는 "일반 네임 스페이스"를 구축했습니다. 특히 새 매개 변수를 추가 할 때 유형 매개 변수를 알기 위해 필요한 모든 유형 매개 변수를 9 개 유형간에 동기화 된 상태로 유지해야하는 것은 지루했습니다. 일반 네임 스페이스를 사용하면 해당 코드를 훨씬 더 쉽게 관리하고 읽을 수 있습니다.


3

Katheleen의 기사 권한을 이해하면 EntityCollection <SomeEntity> 대신 SomeEntity.Collection을 작성할 수 있도록 중첩 클래스를 사용할 것을 제안합니다. 제 생각에는 타이핑을 절약하는 것은 논란의 여지가있는 방법입니다. 실제 응용 프로그램 컬렉션에서 구현에 약간의 차이가있을 것이라고 확신하므로 어쨌든 별도의 클래스를 만들어야합니다. 클래스 이름을 사용하여 다른 클래스 범위를 제한하는 것은 좋은 생각이 아니라고 생각합니다. 지능을 오염시키고 클래스 간의 종속성을 강화합니다. 네임 스페이스 사용은 클래스 범위를 제어하는 ​​표준 방법입니다. 그러나 @hazzen 주석과 같은 중첩 클래스의 사용은 잘못된 디자인의 신호 인 수많은 중첩 클래스가없는 한 허용됩니다.


1

구현 세부 사항을 숨기기 위해 중첩 클래스를 자주 사용합니다. Eric Lippert의 답변 예 :

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

이 패턴은 제네릭을 사용하면 더욱 좋아집니다. 두 가지 멋진 예를 보려면 이 질문 을 참조하십시오 . 그래서 결국

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

대신에

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

또한 일반 목록을 가질 수 Equality<Person>있지만EqualityComparer<Person, int>

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

어디로

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

불가능합니다. 이것이 상위 클래스에서 상속 된 중첩 클래스의 이점입니다.

또 다른 경우 (동일한 특성-구현 숨기기)는 단일 클래스에 대해서만 클래스의 멤버 (필드, 속성 등)에 액세스 할 수 있도록 만들려는 경우입니다.

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

중첩 클래스에 대해 아직 언급되지 않은 또 다른 용도는 제네릭 유형의 분리입니다. 예를 들어, 다양한 수의 매개 변수가있는 메서드와 이러한 매개 변수 중 일부에 대한 값을 가져오고 더 적은 매개 변수를 사용하여 대리자를 생성 할 수있는 몇 가지 일반적인 정적 클래스 패밀리가 있다고 가정합니다. 예를 들어, 하나의 기원은 걸릴 수 정적 메소드 갖도록 Action<string, int, double>수율을 String<string, int>는 AS 3.5 통과 제공된 액션을 호출 할 double; 하나는 한는 걸릴 수 정적 메소드가하실 수 Action<string, int, double>및 수율 Action<string>, 전달 7은 AS int5.3는 AS를 double. 일반 중첩 클래스를 사용하여 다음과 같은 메서드 호출을 구성 할 수 있습니다.

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

또는 각 표현식의 후자 유형은 전자가 할 수없는 경우에도 유추 할 수 있기 때문입니다.

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

중첩 된 제네릭 형식을 사용하면 전체 형식 설명의 어느 부분에 적용 할 수있는 대리자를 알 수 있습니다.


1

중첩 클래스는 다음 요구 사항에 사용할 수 있습니다.

  1. 데이터 분류
  2. 메인 클래스의 로직이 복잡하고 클래스를 관리하기 위해 하위 객체가 필요하다고 느낄 때
  3. 클래스의 상태와 존재가 완전히 둘러싸는 클래스에 달려 있다고 생각할 때


0

저는 단일 클래스에 고유 한 예외를 중첩하는 것을 좋아합니다. 다른 곳에서 결코 던져지지 않는 것.

예를 들면 :

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()
    }
}

이렇게하면 프로젝트 파일을 깔끔하게 유지하고 수백 개의 작은 예외 클래스로 가득 차 있지 않습니다.


0

중첩 된 클래스를 테스트해야합니다. 비공개 인 경우 격리 된 상태에서 테스트 할 수 없습니다.

그러나 속성 과 함께InternalsVisibleTo 내부적으로 만들 수 있습니다 . 그러나 이것은 테스트 목적으로 만 내부적으로 비공개 필드를 만드는 것과 같을 것입니다.

따라서 복잡성이 낮은 개인 중첩 클래스 만 구현할 수 있습니다.


0

이 경우 예 :

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);
        }
    }
}

왜? 솔루션의 코드에 컨텍스트를 추가하여 미래의 독자가 답변의 이유를 이해할 수 있도록하세요.
그랜트 밀러

0

이 개념에 대한 이해를 바탕으로 클래스가 개념적으로 서로 관련 될 때이 기능을 사용할 수 있습니다. 나는 그들 중 일부가 비즈니스 로직을 완성하기 위해 집계 루트 개체를 돕는 DDD 세계에 존재하는 엔티티와 같이 우리 비즈니스에서 완전한 하나의 항목임을 의미합니다.

명확히하기 위해 예제를 통해 보여 드리겠습니다.

Order 및 OrderItem과 같은 두 개의 클래스가 있다고 가정 해보십시오. 주문 클래스에서는 모든 orderItem을 관리 할 것이며 OrderItem에는 설명을 위해 단일 주문에 대한 데이터가 보관되어 있습니다. 아래 클래스를 볼 수 있습니다.

 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();
        }


    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.