컬렉션 표현식 활용

  • 7 minutes to read

이번 글에서는 C# 컬렉션 표현식(Collection Expression)을 통해 코드를 더 깔끔하고 효율적으로 만드는 방법을 살펴보겠습니다. 컬렉션 이니셜라이저(Collection Initializer)와 컬렉션 표현식, 스프레드(Spread) 문법 등을 다루며, 지원되는 컬렉션 타입도 알아보겠습니다. 이 기능들은 C# 개발자의 작업 효율을 높이고 코드 가독성(Readability)을 향상시키는 데 도움이 될 것입니다.

컬렉션 표현식의 도입

C# 12.0에서는 컬렉션을 일관성 있는 문법으로 초기화할 수 있는 컬렉션 표현식(Collection Expression)이 도입되었습니다. 기존의 이니셜라이저 방식과 같은 기능을 하면서도 더 간결한 코드를 작성할 수 있도록 돕습니다. 대괄호 []로 표현되는 컬렉션 표현식은 개발자가 코드를 더 쉽게 이해할 수 있게 합니다.

초기화 방법

다음은 정수 배열을 다양한 방법으로 초기화하는 예시입니다.

var numbers1 = new int[3] { 1, 2, 3 };
var numbers2 = new int[] { 1, 2, 3 };
var numbers3 = new[] { 1, 2, 3 };
int[] numbers4 = { 1, 2, 3 };

이 모든 방식은 같은 결과를 만들어냅니다. 새로운 컬렉션 표현식은 이니셜라이저(Initializer)의 중괄호 {} 대신 대괄호 []를 사용합니다.

List<char> name = [ 'A', 'l', 'i', 'c', 'e' ];

컬렉션 표현식은 var 키워드와 함께 사용할 수 없으며, 반드시 컬렉션 타입을 명시해야 합니다. 다음과 같이 var를 사용할 경우 오류가 발생합니다.

// 오류 CS9176: 컬렉션 표현식에 대한 대상 타입이 없습니다.
// Error CS9176: No target type for the collection expression.
var collection = [1, 2, 3];

일관된 문법으로 컬렉션을 표현하는 컬렉션 표현식은 가독성(Readability)과 유지보수성(Maintainability)을 향상시킵니다.

다양한 형태의 컬렉션 표현식

컬렉션이 비어있을 경우에도 표현식을 사용할 수 있습니다:

int[] emptyCollection = [];

컬렉션 표현식을 통해 기존의 new 키워드 방식보다 메모리를 더 효율적으로 활용할 수 있습니다. 인터페이스(Interface)를 사용해 컬렉션을 초기화할 때에도 컬렉션 표현식을 활용할 수 있습니다.

스프레드 기능

스프레드(Spread) 문법은 다른 컬렉션의 요소를 현재 컬렉션에 결합할 때 간결하게 표현하는 기능입니다.

int[] numbers1 = [1, 2, 3];
int[] numbers2 = [4, 5, 6];

int[] all = [..numbers2, 100, ..numbers1];

Console.WriteLine(string.Join(", ", all));
Console.WriteLine($"길이: {all.Length}");
// 출력(Output):
//   4, 5, 6, 100, 1, 2, 3
//   길이(Length): 7

스프레드 기능은 foreach로 순회할 수 있는 모든 컬렉션에 적용할 수 있습니다.

지원되는 컬렉션 타입

컬렉션 표현식은 대부분의 컬렉션 타입을 지원합니다. BCL(Base Class Library)의 주요 컬렉션이 이미 업데이트되었고, 지원되지 않는 경우에도 속성(Property)이나 빌더 패턴(Builder Pattern)을 활용할 수 있습니다. 다만 현재 사전(Dictionary) 타입은 지원되지 않습니다.

리팩터링 예시

컬렉션 표현식은 다양한 상황에서 유용합니다.

  • 널(Null)이 될 수 없는 컬렉션 타입 필드(Field), 속성(Property), 로컬 변수(Local Variable)를 초기화할 때
  • 컬렉션 타입 매개변수(Parameter)를 받는 메서드(Method)에 인수로 전달할 때

다음은 컬렉션 이니셜라이저를 컬렉션 표현식으로 리팩터링한 예시입니다.

원본 소스는 다음과 같습니다.

public IEnumerable<Customer> Get()
{
    return new List<Customer>
    {
        new Customer { Id = 1, Name = "김태영", City = "서울" },
        new Customer { Id = 2, Name = "박용준", City = "인천" },
        new Customer { Id = 3, Name = "한상훈", City = "경기" }
    };
}

컬렉션 표현식으로 다음과 같이 줄일 수 있습니다.

public IEnumerable<Customer> Get()
{
    return
    [
        new Customer { Id = 1, Name = "김태영", City = "서울" },
        new Customer { Id = 2, Name = "박용준", City = "인천" },
        new Customer { Id = 3, Name = "한상훈", City = "경기" }
    ];
}

식 본문을 사용하여 한 번 더 줄일 수 있습니다.

public IEnumerable<Customer> Get() => [
        new Customer { Id = 1, Name = "김태영", City = "서울" },
        new Customer { Id = 2, Name = "박용준", City = "인천" },
        new Customer { Id = 3, Name = "한상훈", City = "경기" }
    ];

다음은 EventRegistry 개체를 초기화하는 예시입니다.

namespace EventManagement;

public sealed class EventRegistry
{
    private readonly HashSet<Event> _events = [];

    public Guid RegisterEvent(Event newEvent)
    {
        _ = _events.Add(newEvent);
        return newEvent.Id;
    }

    public void RemoveEvent(Guid id)
    {
        _ = _events.RemoveWhere(x => x.Id == id);
    }
}

public record Event(
    bool IsActive,
    string? ErrorDetails)
{
    public Guid Id { get; } = Guid.NewGuid();
}

new HashSet<Event>()[]로 간단하게 표현할 수 있습니다.

Span<T>ReadOnlySpan<T> 지원

컬렉션 표현식은 Span<T>ReadOnlySpan<T>를 지원합니다.

Span<int> numbers = [1, 2, 3, 4, 5];
ReadOnlySpan<char> name = ['A', 'l', 'i', 'c', 'e'];

효율적인 메모리 사용을 통해 성능을 향상시킬 수 있습니다.

컬렉션 표현식 장점

컬렉션 표현식은 기존 이니셜라이저보다 효율적인 코드를 생성합니다.

List<int> someList = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

이니셜라이저는 Add 메서드를 사용하지만, 컬렉션 표현식은 AddRange를 사용해 최적화된 코드를 생성합니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com