컬렉션 식 - C# 언어 참조

컬렉션 식을 사용하여 공통 컬렉션 값을 만들 수 있습니다. 컬렉션 식은 평가 시 다양한 컬렉션 형식에 할당될 수 있는 간결한 구문입니다. 컬렉션 식에는 [] 대괄호 사이에 일련의 요소가 포함되어 있습니다. 다음 예에서는 string 요소의 System.Span<T>를 선언하고 요일로 초기화합니다.

Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
    Console.WriteLine(day);
}

컬렉션 식은 다양한 컬렉션 형식으로 변환될 수 있습니다. 첫 번째 예에서는 컬렉션 식을 사용하여 변수를 초기화하는 방법을 보여 주었습니다. 다음 코드는 컬렉션 식을 사용할 수 있는 다른 여러 위치를 보여 줍니다.

// Initialize private field:
private static readonly ImmutableArray<string> _months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

// property with expression body:
public IEnumerable<int> MaxDays =>
    [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

public int Sum(IEnumerable<int> values) =>
    values.Sum();

public void Example()
{
    // As a parameter:
    int sum = Sum([1, 2, 3, 4, 5]);
}

상수 초기화와 같이 컴파일 시간 상수가 필요한 경우 또는 메서드 인수의 기본값으로 컬렉션 식을 사용할 수 없습니다.

이전 예에서는 모두 컬렉션 식의 요소로 상수가 사용되었습니다. 다음 예와 같이 요소에 대한 변수를 사용할 수도 있습니다.

string hydrogen = "H";
string helium = "He";
string lithium = "Li";
string beryllium = "Be";
string boron = "B";
string carbon = "C";
string nitrogen = "N";
string oxygen = "O";
string fluorine = "F";
string neon = "Ne";
string[] elements = [hydrogen, helium, lithium, beryllium, boron, carbon, nitrogen, oxygen, fluorine, neon];
foreach (var element in elements)
{
    Console.WriteLine(element);
}

spread 요소

컬렉션 식의 컬렉션 값을 인라인하려면 spread 요소..를 사용합니다. 다음 예에서는 모음 컬렉션, 자음 컬렉션 및 문자 "y"를 결합하여 전체 알파벳에 대한 컬렉션을 만듭니다. 이는 다음 중 하나일 수 있습니다.

string[] vowels = ["a", "e", "i", "o", "u"];
string[] consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
                       "n", "p", "q", "r", "s", "t", "v", "w", "x", "z"];
string[] alphabet = [.. vowels, .. consonants, "y"];

spread 요소 ..vowels는 평가 시 5개의 요소("a", "e", "i", "o""u")를 생성합니다. spread 요소 ..consonantsconsonants 배열의 숫자인 20개의 요소를 생성합니다. spread 요소의 변수는 foreach 문을 사용하여 열거 가능해야 합니다. 이전 예에 표시된 것처럼 spread 요소를 컬렉션 식의 개별 요소와 결합할 수 있습니다.

변환

컬렉션 식은 다음을 포함한 다양한 컬렉션 형식으로 변환될 수 있습니다.

Important

컬렉션 식은 변환의 대상 형식에 관계없이 컬렉션 식의 모든 요소를 포함하는 컬렉션을 항상 만듭니다. 예를 들어 변환 대상이면 생성된 코드는 System.Collections.Generic.IEnumerable<T>컬렉션 식을 평가하고 결과를 메모리 내 컬렉션에 저장합니다.

이 동작은 시퀀스가 열거될 때까지 인스턴스화되지 않을 수 있는 LINQ와 다릅니다. 컬렉션 식을 사용하여 열거되지 않는 무한 시퀀스를 생성할 수 없습니다.

컴파일러는 정적 분석을 사용하여 컬렉션 식으로 선언된 컬렉션을 만드는 가장 성능이 좋은 방법을 결정합니다. 예를 들어 빈 컬렉션 식 []은 초기화 후 대상이 수정되지 않는 경우 Array.Empty<T>()로 구현될 수 있습니다. 대상이 a System.Span<T> 또는 System.ReadOnlySpan<T>인 경우 스토리지가 스택 할당될 수 있습니다. 컬렉션 식 기능 사양은 컴파일러가 따라야 하는 규칙을 지정합니다.

많은 API는 여러 컬렉션 형식을 매개 변수로 사용하여 오버로드됩니다. 컬렉션 식을 다양한 식 형식으로 변환할 수 있으므로 이러한 API는 올바른 변환을 지정하기 위해 컬렉션 식에 대한 캐스트가 필요할 수 있습니다. 다음 변환 규칙은 일부 모호성을 해결합니다.

  • Span<T>, ReadOnlySpan<T> 또는 다른 ref struct 형식으로 변환하는 것이 참조가 아닌 구조체 형식으로 변환하는 것보다 낫습니다.
  • 인터페이스가 아닌 형식으로 변환하는 것이 인터페이스 형식으로 변환하는 것보다 낫습니다.

컬렉션 식이 Span 또는 ReadOnlySpan으로 변환되면 범위 개체의 안전한 컨텍스트는 범위에 포함된 모든 요소의 안전한 컨텍스트에서 가져옵니다. 자세한 규칙은 컬렉션 식 사양을 참조하세요.

컬렉션 작성기

컬렉션 식은 잘 작동하는 모든 컬렉션 형식 에서 작동합니다. 잘 동작하는 컬렉션에는 다음과 같은 특징이 있습니다.

  • 개수 가능한 컬렉션의 Count 값은 Length열거할 때 요소 수와 동일한 값을 생성합니다.
  • 네임스페이스의 System.Collections.Generic 형식은 부작용이 없는 것으로 추정됩니다. 따라서 컴파일러는 이러한 형식을 중간 값으로 사용할 수 있지만 그렇지 않으면 노출되지 않는 시나리오를 최적화할 수 있습니다.
  • 컬렉션에서 적용 가능한 .AddRange(x) 일부 멤버를 호출하면 반복하고 열거된 모든 값을 컬렉션에 개별적으로 추가하는 것과 동일한 최종 값 x.Add발생합니다.

.NET 런타임의 모든 컬렉션 형식은 잘 동작합니다.

Warning

사용자 지정 컬렉션 형식이 잘 동작하지 않으면 컬렉션 식과 함께 해당 컬렉션 형식을 사용할 때의 동작은 정의되지 않습니다.

형식은 메서드를 작성하고 Create() 컬렉션 형식에 적용하여 작성기 메서드를 System.Runtime.CompilerServices.CollectionBuilderAttribute 나타내도록 컬렉션 식 지원을 선택합니다. 예를 들어, 80자의 고정 길이 버퍼를 사용하는 애플리케이션을 생각해 보세요. 해당 클래스는 다음 코드와 유사할 수 있습니다.

public class LineBuffer : IEnumerable<char>
{
    private readonly char[] _buffer = new char[80];

    public LineBuffer(ReadOnlySpan<char> buffer)
    {
        int number = (_buffer.Length < buffer.Length) ? _buffer.Length : buffer.Length;
        for (int i = 0; i < number; i++)
        {
            _buffer[i] = buffer[i];
        }
    }

    public IEnumerator<char> GetEnumerator() => _buffer.AsEnumerable<char>().GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => _buffer.GetEnumerator();

    // etc
}

다음 샘플과 같이 컬렉션 식과 함께 사용하려고 합니다.

LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];

LineBuffer 형식은 IEnumerable<char>를 구현하므로 컴파일러는 이를 char 항목의 컬렉션으로 인식합니다. 구현된 System.Collections.Generic.IEnumerable<T> 인터페이스의 형식 매개 변수는 요소 형식을 나타냅니다. LineBuffer 개체에 컬렉션 식을 할당하려면 애플리케이션에 두 가지 항목을 추가해야 합니다. 먼저, Create 메서드를 포함하는 클래스를 만들어야 합니다.

internal static class LineBufferBuilder
{
    internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}

Create 메서드는 LineBuffer 개체를 반환해야 하며 ReadOnlySpan<char> 형식의 단일 매개 변수를 사용해야 합니다. ReadOnlySpan의 형식 매개 변수는 컬렉션의 요소 형식과 일치해야 합니다. 제네릭 컬렉션을 반환하는 작성기 메서드는 제네릭 ReadOnlySpan<T>를 매개 변수로 갖습니다. 메서드는 액세스 가능하고 static이어야 합니다.

마지막으로 LineBuffer 클래스 선언에 CollectionBuilderAttribute를 추가해야 합니다.

[CollectionBuilder(typeof(LineBufferBuilder), "Create")]

첫 번째 매개 변수는 Builder 클래스의 이름을 제공합니다. 두 번째 특성은 작성기 메서드의 이름을 제공합니다.