CodeGym /행동 /C# SELF /인터페이스의 static 멤버

인터페이스의 static 멤버

C# SELF
레벨 24, 레슨 1
사용 가능

1. 소개

예전엔 인터페이스가 사립학교 규칙처럼 엄격했어: 시그니처만 있고, 필드도 없고, 구현도 없고, static 멤버도 없었지! 하지만 .NET은 계속 발전하고, 프로그래밍 언어도 살아있는 생물처럼 새로운 도전에 맞춰 진화해야 해.

새로운 C# 버전이 나오면서 인터페이스도 새로운 트릭을 배웠어. 그 중 가장 눈에 띄는 게 바로 static 멤버야. 이제 인터페이스에도 static 메서드, 프로퍼티, 이벤트를 넣을 수 있어. 그리고 C# 11부터는 static abstract 메서드도 선언할 수 있어서, 이 인터페이스를 구현하는 타입이 반드시 static 메서드를 구현해야 해.

이건 진짜 패러다임의 큰 변화야. 제네릭이나 객체지향 프로그래밍을 다루는 방식이 달라졌거든.

쉽게 말하면: 인터페이스의 static 멤버는 "공용" 멤버야. 객체 인스턴스가 아니라 인터페이스 타입(혹은 구현 타입) 자체로 접근하는 거지.

예전엔 클래스, 구조체, enum만 static 메서드나 프로퍼티를 가질 수 있었는데, 이제 인터페이스도 가능해졌어.

어떻게 생겼냐고? 문법 예시


public interface IMyMath
{
    static int Add(int a, int b) => a + b; // static 메서드 (기본 구현)

    static abstract int Multiply(int a, int b); // 구현 타입에서 반드시 구현해야 함
}
  • static — 타입 레벨에서 접근, 인스턴스 아님.
  • static abstract — "반드시" static 메서드를 구현해야 한다는 계약.
  • 이제 인터페이스(클래스처럼)에도 static 메서드, 프로퍼티, 이벤트 선언 가능. 상수도 만들 수 있어. 하지만 여전히 인스턴스 필드나 static 필드는 못 넣어(상수 제외).

static 멤버의 역할은 범용적인 "연산"을 선언하는 거야. 예를 들어, 객체 컬렉션이 있는데 어떤 타입이 인터페이스를 구현하는지 몰라도 "비교" 같은 걸 호출하고 싶을 때, static abstract 멤버로 가능해.

2. 인터페이스에 구현이 있는 static 메서드

이게 왜 필요해?

고전적인 문제: 인스턴스 메서드(객체로 뭔가 하는 것)뿐 아니라 static 메서드(예: 문자열에서 객체 만들기, 두 객체를 static하게 비교하기)도 선언하고 싶을 때가 있어. 예전엔 Factory, Comparer, Helper 같은 패턴으로 우회했는데, 이제 인터페이스에서 바로 표현 가능해.

특히 Generics나 임의 타입을 다루는 알고리즘에서 엄청 중요해졌어:

  • 범용 연산자 구현 (예: 더하기, 비교 등).
  • 제네릭 코드 제한: "static 메서드나 연산자가 있는 타입만…" 같은 조건.
  • 직렬화/역직렬화: 코드 작성 시 타입을 몰라도 문자열에서 객체를 만들고 싶을 때.

구현이 있는 static 메서드

C# 8부터 인터페이스에 구현이 있는 static 메서드를 넣을 수 있게 됐어. 클래스의 static 메서드랑 비슷해.


public interface IUtility
{
    static void PrintHello()
    {
        Console.WriteLine("인터페이스에서 Hello!");
    }
}

이런 메서드는 이렇게 호출해: IUtility.PrintHello();

인터페이스에 논리적으로 속하지만 특정 구현과는 상관없는 유틸 함수에 딱 좋아. 예: 타입 전체 통계, CreateDefault 같은 팩토리 메서드, 공통 유효성 검사 등.

특징: 인터페이스의 static 멤버는 클래스에서 "오버라이드" 안 됨

인터페이스에 static void Method() { ... }를 선언하면, 구현 클래스에서 똑같은 static 메서드를 선언해도 오버라이드가 아냐! 그냥 이름만 같고 완전히 별개인 두 메서드야. "가상 static 메서드"가 아니란 뜻.

3. static abstract 멤버

C# 11부터 인터페이스에 static abstract 메서드를 선언할 수 있어. 이건 "이 인터페이스를 구현하는 클래스나 구조체는 반드시 이 시그니처의 static 멤버를 구현해야 한다"는 뜻이야.

예시:


public interface IParsable<T>
{
    static abstract T Parse(string s);
}

이 인터페이스를 구현하는 타입은 static 메서드 Parse(string s)를 반드시 구현해야 해.

클래스에서 이런 인터페이스 구현하기


public class Temperature : IParsable<Temperature>
{
    public int Value { get; set; }

    // static 구현!
    public static Temperature Parse(string s)
    {
        var temp = new Temperature();
        temp.Value = int.Parse(s);
        return temp;
    }
}

어떻게 동작하냐면?

이게 제네릭 코드에서 특히 재밌어져:


public static T ParseFromString<T>(string s) where T : IParsable<T>
{
    return T.Parse(s);
}

// 사용 예시:
var temp = ParseFromString<Temperature>("42");

이제 "static" 동작을 구현한 아무 타입이나 받아서 진짜 범용 코드를 쓸 수 있어!

4. 인터페이스의 static 멤버 vs. 일반 클래스 static 멤버

특징 클래스 static 멤버 인터페이스 static 멤버
상속됨 아니오 아니오, 하지만 인터페이스 계약 내에서 구현됨
구현 필수? 아니오 static abstract일 때만 필수
Generics에서 사용 아니오 (C# 11 전까지) 가능 (static abstract부터)
기본 구현 가능? 가능 가능
오버라이드 아니오 아니오, 반드시 구현만 필요
호출 시 가시성 타입 이름으로 인터페이스 타입이나 구현 타입으로

7. 실제 예시

이제 실제로 우리 작은 학습용 앱에 이 기능을 어떻게 적용할 수 있는지 보자.

"IPrintable" 인터페이스가 있다고 해보자:


public interface IPrintable
{
    void Print();
    static void PrintAll(IEnumerable<IPrintable> items)
    {
        foreach (var item in items)
        {
            item.Print();
        }
    }
}

이제 이렇게 쉽게 호출할 수 있어:


var documents = new List<IPrintable>
{
    new Invoice { Number = "INV-001" },
    new Receipt { Number = "RC-007" }
};
IPrintable.PrintAll(documents); // 인터페이스의 static 메서드!

이런 구조는 인터페이스 구현 전체에 "그룹 작업" 할 때 완전 좋아.

좀 더 고급 예시: 숫자 타입의 범용 더하기

인터페이스가 있다고 해보자:


public interface IAddable<T>
{
    static abstract T Add(T left, T right);
}

정수용(래퍼 클래스) 구현:


public struct MyInt : IAddable<MyInt>
{
    public int Value { get; }
    public MyInt(int val) => Value = val;
    public static MyInt Add(MyInt left, MyInt right) => new MyInt(left.Value + right.Value);
}

그리고, 두 숫자 타입 T를 더하는 범용 함수:


public static T Sum<T>(T a, T b) where T : IAddable<T>
{
    return T.Add(a, b);
}

// 사용 예시:
var x = new MyInt(5);
var y = new MyInt(6);
var z = Sum(x, y); // z.Value == 11

이런 범용성을 위해서 인터페이스 static 멤버 지원이 생긴 거야!

8. 흔한 실수와 특징

새로운 기능이 항상 예제처럼 쉽진 않아. 초보가 헷갈릴 만한 포인트 몇 개 정리해줄게:

인터페이스 static 메서드는 구현 클래스에 "상속"되지 않아. 인터페이스에 static void Foo()가 있으면, MyClass.Foo()와 IMyInterface.Foo()는 완전히 별개 메서드야.

static abstract는 반드시 구현해야 해. 까먹으면 컴파일러가 "인터페이스 완전히 구현 안 됐음"이라고 알려줄 거야.

Generic 제약: static abstract 멤버를 쓰려면 generic 파라미터에 인터페이스로 제한 걸어야 해 (where T : IMyInterface).

아직 모든 툴이 이 기능을 완벽히 지원하진 않아. 예를 들어 Rider, VS Code, 구버전 Roslyn 분석기는 .NET이 C# 11+를 지원하지 않으면 static abstract 멤버를 제대로 못 보여줄 수도 있어.

인터페이스 extension 메서드랑 헷갈리지 마: extension 메서드는 따로 구현되고, static 멤버처럼 동작하지 않아.

2
과제
C# SELF, 레벨 24, 레슨 1
잠금
인터페이스에 static 메서드 만들기
인터페이스에 static 메서드 만들기
2
과제
C# SELF, 레벨 24, 레슨 1
잠금
인터페이스를 사용한 static 메서드
인터페이스를 사용한 static 메서드
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION