C# 클래스의 정적 멤버 이해와 활용

C#에서 클래스는 필드, 속성, 메서드로 구성되며 일반적으로 객체를 생성한 후 해당 객체를 통해 멤버에 접근한다. 이러한 멤버들은 각각의 객체가 독립적으로 소유하는 인스턴스 멤버이다.

예를 들어 제품 정보를 담은 Product 클래스가 있다고 가정하자. 이 클래스에는 제품명을 저장하는 name 필드가 있다. 두 개의 제품 객체(xphone과 mphone)를 생성하고 각각 다른 이름으로 초기화하면, xphone.name은 "XPhone 10"을 반환하고, mphone.name은 "MPhone 6X"를 반환한다. 이런 멤버들은 특정 객체에 종속되어 있는 인스턴스 멤버라고 할 수 있다.

하지만 데이터베이스 연결처럼 자주 사용되는 기능이나 모든 객체가 공통으로 사용해야 하는 값의 경우 매번 객체를 생성해서 접근하는 것은 비효율적이다. 메모리 낭비와 성능 저하를 초래할 뿐 아니라 데이터 공유도 어렵다. 이러한 문제를 해결하기 위해 C#에서는 정적(static) 멤버를 제공한다.

정적 멤버의 특징

정적 멤버는 프로그램 시작 시점에 메모리에 로드되며, 사용 여부와 관계없이 프로그램 종료 전까지 메모리에 유지된다. 또한 모든 객체가 동일한 값을 참조하므로 데이터 공유와 빠른 실행이 가능하다.

정적 멤버는 다음과 같은 세 가지로 구분된다:

  • 정적 필드(Static Field)
  • 정적 속성(Static Property)
  • 정적 메서드(Static Method)

정적 필드 예제

정적 필드는 static 키워드를 사용하여 선언하며, 클래스명.필드명 형식으로 접근한다. 아래 예제는 제품 클래스에서 회사명을 정적 필드로 선언한 경우이다.

namespace T10
{
    class Product
    {
        public string name;           // 제품명 (인스턴스 필드)
        public string category;       // 카테고리 (인스턴스 필드)
        public double price;          // 가격 (인스턴스 필드)
        
        public static string companyName = "STTF Tech"; // 회사명 (정적 필드)
    }
}

프로그램 실행 부분에서는 다음과 같이 사용한다:

namespace T10
{
    class Program
    {
        static void Main(string[] args)
        {
            Product xphone = new Product();
            xphone.name = "XPhone 10";
            xphone.category = "Flagship Phone";
            xphone.price = 9888;

            Product mphone = new Product();
            mphone.name = "MPhone 6X";
            mphone.category = "Budget Phone";
            mphone.price = 999;

            // 정적 필드 접근
            Console.WriteLine("{0}의 {1} {2}, 가격: {3:C}", 
                Product.companyName, xphone.category, xphone.name, xphone.price);

            // 회사명 변경 - 모든 객체에 영향
            Product.companyName = "STTF Technology Co., Ltd.";
            
            Console.WriteLine("{0}의 {1} {2}, 가격: {3:C}", 
                Product.companyName, mphone.category, mphone.name, mphone.price);
        }
    }
}

정적 필드와 인스턴스 필드 비교

정적 필드는 모든 객체가 공유하는 단일 메모리 공간을 사용하며, 인스턴스 필드는 각 객체마다 별도의 메모리 공간을 갖는다. 다음 예제를 통해 차이점을 확인할 수 있다:

class CounterExample
{
    public int instanceValue = 0;      // 인스턴스 필드
    public static int staticValue = 0; // 정적 필드

    static void Main()
    {
        CounterExample obj1 = new CounterExample();
        obj1.instanceValue++;
        CounterExample.staticValue++;

        CounterExample obj2 = new CounterExample();
        obj2.instanceValue++;
        CounterExample.staticValue++;

        Console.WriteLine($"obj1 인스턴스 값: {obj1.instanceValue}"); // 1
        Console.WriteLine($"obj2 인스턴스 값: {obj2.instanceValue}"); // 1
        Console.WriteLine($"정적 값: {CounterExample.staticValue}");   // 2
    }
}

정적 메서드 활용

정적 메서드 역시 static 키워드로 선언하며, 클래스명.메서드명() 형태로 호출한다. 정적 메서드 내에서는 인스턴스 멤버에 접근할 수 없지만, 정적 멤버에는 자유롭게 접근 가능하다.

class ProductInfo
{
    public string productName;
    public static string manufacturer = "TechCorp";

    // 인스턴스 메서드
    public void ShowProduct()
    {
        Console.WriteLine($"제품: {productName}, 제조사: {manufacturer}");
    }

    // 정적 메서드
    public static void ShowManufacturerInfo()
    {
        Console.WriteLine($"{manufacturer} - 글로벌 테크놀로지 리더");
        // Console.WriteLine(productName); // 오류: 인스턴스 멤버 접근 불가
    }
}

class Program
{
    static void Main()
    {
        ProductInfo laptop = new ProductInfo();
        laptop.productName = "Gaming Laptop";
        
        laptop.ShowProduct();                    // 인스턴스 메서드 호출
        ProductInfo.ShowManufacturerInfo();      // 정적 메서드 호출
    }
}

정적 멤버 사용 시 주의사항

  1. 메모리 관리: 정적 멤버는 프로그램 종료 시까지 메모리에 남아 있으므로 과도한 사용은 메모리 낭비를 초래할 수 있다.
  2. 접근 제한: 정적 메서드에서는 인스턴스 멤버에 접근할 수 없지만, 인스턴스 메서드에서는 정적 멤버에 접근 가능하다.
  3. 상태 공유: 모든 객체가 동일한 정적 멤버를 공유하므로 상태 변경 시 주의가 필요하다.

태그: csharp static-members class-design memory-management

6월 20일 06:27에 게시됨