Study/C#

[C#] 54. namespace와 using 그리고 partial의 사용법

v명월v 2021. 10. 21. 17:23

안녕하세요. 명월입니다.


이 글은 C#의 namespace와 using 그리고 partial의 사용법에 대한 글입니다.


namespace

C#에서의 가장 최소 단위의 객체(Object)를 클래스라고 설명했습니다.

link - [C#] 09. 클래스 생성하는 방법(생성자, 소멸자)


우리가 실행 함수(Main)를 사용하더라도 그 함수는 반드시 클래스에 포함되어야 합니다. 즉, 함수 만을 사용할 수 없습니다.

그래서 우리가 프로그램을 다루다 보면 많은 라이브러리를 연결하고 많은 소스를 작성하게 됩니다. 그럴 경우, 클래스 명이 모두 다르게 작성할 수 있을까? 참고로 클래스는 오버로드(overload 같은 이름의 다른 처리)가 적용되지 않습니다.

같은 이름의 클래스를 작성하면 반드시 에러가 발생합니다.

그렇다면 우리가 프로그램을 작성할 때는 그것을 조심하게 작성한다고 해도, 오픈 라이브러리를 연결해 쓸 때, 그 라이브러리 안에 있는 클래스 명이 절대 중복되지 않을까? 가능성이 충분히 있습니다.

그럼 우리는 라이브러리를 연결해야 사용해야 하는데 클래스가 중복되어 실행이 되지 않습니다.


그것을 방지하기 위해 namespace가 있습니다.

using System;
// Test1의 이름인 namespace
namespace Test1
{
  // Node 클래스
  class Node
  {
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Test1.Node");
    }
  }
}
// Test2의 이름인 namespace
namespace Test2
{
  // Node 클래스
  class Node
  {
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Test2.Node");
    }
  }
}
// Example의 이름인 namespace
namespace Example
{
  // 실행 함수가 있는 클래스
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // Test1의 namespace 안에 있는 Node 클래스
      var test1 = new Test1.Node();
      // 함수 실행
      test1.Print();
      // Test2의 namespace 안에 있는 Node 클래스
      var test2 = new Test2.Node();
      // 함수 실행
      test2.Print();

      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

namespace를 두개로 나누어서 class를 생성하면 같은 class 이름이라도 생성이 가능합니다. 정확히는 Test1.Node 이름과 Test2.Node 이름이기 때문에 다른 이름입니다.

이렇게 각자의 namespace를 유니크로 지정해 놓으면 다른 라이브러리와 클래스 이름이 충돌할 일이 줄어듭니다.

using

그런데 이 namespace는 중복 선언이 가능합니다.

using System;
// A의 이름인 namespace
namespace A
{
  // B의 이름인 namespace
  namespace B
  {
    // C의 이름인 namespace
    namespace C
    {
      // 클래스
      class Node
      {
        // 콘솔 출력 함수
        public void Print()
        {
          // 콘솔 출력
          Console.WriteLine("Hello world");
        }
      }
    }
  }
}
// Example의 이름인 namespace
namespace Example
{
  // 실행 함수가 있는 클래스
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // A의 namespace 안에, B의 namespace 안에, C의 namespace 안에 있는 Node 클래스(namespace가 생략됨)
      var test1 = new A.B.C.Node();
      // 함수 실행
      test1.Print();

      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

위 예제에서는 namespace를 A,B,C로 표현했지만 만약 이 명명이 길다면, 인스턴스를 생성할 때, 구문이 길어 지겠네요.

그것을 축약하기 위해서 using 키워드를 사용합니다.

using System;
// A의 이름인 namespace
namespace A
{
  // B의 이름인 namespace
  namespace B
  {
    // C의 이름인 namespace
    namespace C
    {
      // 클래스
      class Node
      {
        // 콘솔 출력 함수
        public void Print()
        {
          // 콘솔 출력
          Console.WriteLine("Hello world");
        }
      }
    }
  }
}
// Example의 이름인 namespace
namespace Example
{
  // 여기 이후의 소스는 A.B.C의 네임스페이스 생략이 가능하다.
  using A.B.C;
  // 실행 함수가 있는 클래스
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // A의 namespace 안에, B의 namespace 안에, C의 namespace 안에 있는 Node 클래스
      var test1 = new Node();
      // 함수 실행
      test1.Print();

      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

using을 사용해서 namespace를 지정하면 using을 작성한 코드 라인 이후에는 A.B.C를 추가하지 않아도 A.B.C의 클래스를 사용할 수 있습니다.

위 예제에서는 제가 설명하기 쉽게 하나의 페이지로 설명했으나 보통은 클래스 별로 파일을 나누고 namespace별로 폴더를 구분하기 때문에 using을 소스의 가장 최상단에 표기합니다.


그렇다면 최초의 예제처럼 같은 이름의 클래스는 어떻게 처리할까?

using System;
// Test1의 이름인 namespace
namespace Test1
{
  // Node 클래스
  class Node
  {
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Test1.Node");
    }
  }
}
// Test2의 이름인 namespace
namespace Test2
{
  // Node 클래스
  class Node
  {
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Test2.Node");
    }
  }
}
// Example의 이름인 namespace
namespace Example
{
  // 여기 이후의 소스는 Test1의 네임스페이스 생략이 가능하다.
  using Test1;
  // 여기 이후의 소스는 Test2의 네임스페이스 생략이 가능하다.
  using Test2;
  // 실행 함수가 있는 클래스
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // Test1의 namespace 안에 있는 Node 클래스
      var test1 = new Test1.Node();
      // 함수 실행
      test1.Print();
      // Test2의 namespace 안에 있는 Node 클래스
      var test2 = new Test2.Node();
      // 함수 실행
      test2.Print();

      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

그렇습니다. 이 경우에는 Node의 클래스 구분이 되지 않기 때문에 using를 사용했어도 앞에 namespace 이름을 넣어야 합니다.


그리고 클래스가 static으로 이루어져 있고 클래스의 안에 함수가 모두 static으로 이루어져 있을 경우도 있습니다.

일명 유틸 함수 클래스라고도 이야기하죠.

using System;
// Util의 이름인 namespace
namespace Util
{
  // Common 정적 클래스
  static class Common
  {
    // 콘솔 출력 함수
    public static void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Hello world");
    }
  }
}
// Example의 이름인 namespace
namespace Example
{
  // Util namespace의 Common 클래스
  using static Util.Common;
  using static System.Console;
  // 실행 함수가 있는 클래스
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // using static이 선언되었기 때문에 내부 함수처럼 사용 가능하다.
      Print();

      // 아무 키나 누르시면 종료합니다.
      WriteLine("Press any key...");
      ReadLine();
    }
  }
}

그렇습니다. namespace와 클래스 명까지 생략이 가능합니다.

아래에 우리가 습관처럼 사용했던 Console.WriteLine도 Console를 생략했습니다.

partial

원래는 명명법에 의해 namespace는 폴더명, class는 파일 명과 일치시켜야 하는 규약이 있습니다.

하나의 파일에 모든 소스 내용을 작성하지는 않습니다.


그런데 하나의 파일에 하나의 클래스를 작성한다고 해도 사양에 따라 그 클래스의 크기가 엄청나게 길어지는 경우도 있습니다. 클래스 하나가 몇만줄 할 수도 있다는 뜻입니다.

그럴 경우, 마찬가지로 가독성이 매우 떨어지기 때문에 파일을 분할해서 클래스를 작성하고 싶은 경우도 있습니다.

using System;
namespace Example
{
  // Program 클래스 분할
  partial class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 함수 호출
      Print();

      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}
using System;
namespace Example
{
  // Program 클래스 분할
  partial class Program
  {
    // 콘솔 출력 함수
    public static void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Hello world");
    }
  }
}

partial 키워드를 사용하면 두개의 파일에 하나의 클래스를 분할해서 작성하는 것이 가능합니다. 여기서 주의할 점은 partial이 없는 클래스에 partial를 사용해서 클래스를 확장한다는 개념은 아닙니다.

그러니깐 String 클래스에 partial class String을 사용한다고 해도 확장이 되는 개념이 아닙니다. 사실 클래스가 맘대로 확장이 되면 안되는 것이지요. 반대로 이야기하면 partial 키워드를 사용하면 어디서든 확장이 된다는 개념입니다.

즉, 라이브러리 배포용(dll)을 개발할 때에는 사용을 주의해야 하겠습니다.


여기까지 C#의 namespace와 using 그리고 partial의 사용법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.