[c#] ArraySegment <T> 클래스의 용도는 무엇입니까?

클래스 ArraySegment<byte>를 서브 클래 싱하는 동안 방금 유형을 발견했습니다 MessageEncoder.

이제 주어진 배열의 세그먼트이고 오프셋을 취하고 열거 할 수 없으며 인덱서가 없다는 것을 이해하지만 여전히 사용법을 이해하지 못합니다. 누군가 예를 들어 설명해 주시겠습니까?



답변

ArraySegment<T>.NET 4.5 + 및 .NET Core 에서 다음을 구현함에 따라 훨씬 더 유용 해졌습니다 .

  • IList<T>
  • ICollection<T>
  • IEnumerable<T>
  • IEnumerable
  • IReadOnlyList<T>
  • IReadOnlyCollection<T>

인터페이스를 전혀 구현하지 않은 .NET 4 버전 과는 대조적 입니다.

이제 클래스는 LINQ의 멋진 세계에 참여할 수 있으므로 콘텐츠 쿼리, 원래 배열에 영향을주지 않고 콘텐츠 반전, 첫 번째 항목 가져 오기 등과 같은 일반적인 LINQ 작업을 수행 할 수 있습니다.

var array = new byte[] { 5, 8, 9, 20, 70, 44, 2, 4 };
array.Dump();
var segment = new ArraySegment<byte>(array, 2, 3);
segment.Dump(); // output: 9, 20, 70
segment.Reverse().Dump(); // output 70, 20, 9
segment.Any(s => s == 99).Dump(); // output false
segment.First().Dump(); // output 9
array.Dump(); // no change


답변

  1. IO 클래스에 대한 버퍼 파티셔닝-동시 읽기 및 쓰기 작업에 동일한 버퍼를 사용하고 전체 작업을 설명하는 단일 구조를 전달할 수 있습니다.
  2. 함수 설정-수학적으로 말하면이 새로운 구조를 사용하여 연속적인 하위 집합을 나타낼 수 있습니다. 즉, 기본적으로 배열의 파티션을 만들 수 있지만 모든 배당률과 짝수를 모두 나타낼 수는 없습니다. The1이 제안한 전화 티저는 ArraySegment 파티셔닝과 트리 구조를 사용하여 우아하게 해결할 수있었습니다. 마지막 숫자는 먼저 트리 깊이를 횡단하여 기록 할 수 있습니다. 이것은 제가 믿는 메모리와 속도 측면에서 이상적인 시나리오였습니다.
  3. 멀티 스레딩-이제 세그먼트 배열을 제어 게이트로 사용하면서 동일한 데이터 소스에서 작동하도록 여러 스레드를 생성 할 수 있습니다. 이산 계산을 사용하는 루프는 이제 매우 쉽게 팜 아웃 할 수 있으며, 최신 C ++ 컴파일러가 코드 최적화 단계로 수행하기 시작하는 작업입니다.
  4. UI 분할-분할 된 구조를 사용하여 UI 디스플레이를 제한합니다. 이제 표시 기능에 빠르게 적용 할 수있는 데이터 페이지를 나타내는 구조를 저장할 수 있습니다. 단일 연속 배열을 사용하여 개별 뷰를 표시하거나 선형 데이터 저장소를 노드 컬렉션 세그먼트로 분할하여 TreeView의 노드와 같은 계층 구조를 표시 할 수 있습니다.

이 예제에서는 원본 배열, Offset 및 Count 속성을 사용하는 방법과 ArraySegment에 지정된 요소를 반복하는 방법을 살펴 봅니다.

using System;

class Program
{
    static void Main()
    {
        // Create an ArraySegment from this array.
        int[] array = { 10, 20, 30 };
        ArraySegment<int> segment = new ArraySegment<int>(array, 1, 2);

        // Write the array.
        Console.WriteLine("-- Array --");
        int[] original = segment.Array;
        foreach (int value in original)
        {
            Console.WriteLine(value);
        }

        // Write the offset.
        Console.WriteLine("-- Offset --");
        Console.WriteLine(segment.Offset);

        // Write the count.
        Console.WriteLine("-- Count --");
        Console.WriteLine(segment.Count);

        // Write the elements in the range specified in the ArraySegment.
        Console.WriteLine("-- Range --");
        for (int i = segment.Offset; i < segment.Count+segment.Offset; i++)
        {
            Console.WriteLine(segment.Array[i]);
        }
    }
}

ArraySegment 구조-그들은 무엇을 생각하고 있었습니까?


답변

배열에 대한 참조를 유지하고 인덱스 범위를 저장하는 것 외에는 아무것도하지 않는 작은 군인 구조체입니다. 약간 위험합니다. 어레이 데이터의 복사본을 만들지 않으며 어떤 식 으로든 어레이를 불변으로 만들거나 불변성에 대한 필요성을 표현하지 않습니다. 보다 일반적인 프로그래밍 패턴은 .NET BeginRead () 메서드, String.SubString (), Encoding.GetString () 등에서 수행되는 것처럼 배열과 길이 변수 또는 매개 변수를 유지하거나 전달하는 것입니다.

웹 소켓에서 작업하고 WCF가 좋아하는 특정 Microsoft 프로그래머처럼 보이는 것을 제외하고는 .NET Framework 내에서 많이 사용되지 않습니다. 원하는 경우 적절한 지침이 될 수 있습니다. .NET 4.6에서 엿보기를 수행했으며 추가 된 MemoryStream.TryGetBuffer () 메서드가이를 사용합니다. out내가 가정 하는 두 가지 주장 보다 선호 됩니다.

일반적으로 슬라이스에 대한보다 보편적 인 개념은 Mads Torgersen 및 Stephen Toub와 같은 주요 .NET 엔지니어의 위시리스트에서 높습니다. 후자는 array[:]얼마 전에 구문 제안을 시작했으며, 이 Roslyn 페이지 에서 그들이 무엇을 생각했는지 알 수 있습니다 . 나는 이것이 궁극적으로 CLR 지원을 얻는 것이라고 생각합니다. 이것은 C # 버전 7 afaik에 대해 적극적으로 고려되고 있으며 System.Slices를 주시하십시오 .

업데이트 : 데드 링크, 이것은 버전 7.2에서 Span 으로 제공되었습니다 .

Update2 : Range 및 Index 유형과 Slice () 메서드를 사용하여 C # 버전 8.0에서 추가 지원.


답변

래퍼 클래스는 무엇입니까? 데이터를 임시 버퍼에 복사하는 것을 피하기 위해서입니다.

public class SubArray<T> {
        private ArraySegment<T> segment;

        public SubArray(T[] array, int offset, int count) {
            segment = new ArraySegment<T>(array, offset, count);
        }
        public int Count {
            get { return segment.Count; }
        }

        public T this[int index] {
            get {
               return segment.Array[segment.Offset + index];
            }
        }

        public T[] ToArray() {
            T[] temp = new T[segment.Count];
            Array.Copy(segment.Array, segment.Offset, temp, 0, segment.Count);
            return temp;
        }

        public IEnumerator<T> GetEnumerator() {
            for (int i = segment.Offset; i < segment.Offset + segment.Count; i++) {
                yield return segment.Array[i];
            }
        }
    } //end of the class

예:

byte[] pp = new byte[] { 1, 2, 3, 4 };
SubArray<byte> sa = new SubArray<byte>(pp, 2, 2);

Console.WriteLine(sa[0]);
Console.WriteLine(sa[1]);
//Console.WriteLine(b[2]); exception

Console.WriteLine();
foreach (byte b in sa) {
    Console.WriteLine(b);
}

Ouput :

3
4

3
4


답변

ArraySegment는 생각보다 훨씬 더 유용합니다. 다음 단위 테스트를 실행하고 놀라실 준비를하세요!

    [TestMethod]
    public void ArraySegmentMagic()
    {
        var arr = new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

        var arrSegs = new ArraySegment<int>[3];
        arrSegs[0] = new ArraySegment<int>(arr, 0, 3);
        arrSegs[1] = new ArraySegment<int>(arr, 3, 3);
        arrSegs[2] = new ArraySegment<int>(arr, 6, 3);
        for (var i = 0; i < 3; i++)
        {
            var seg = arrSegs[i] as IList<int>;
            Console.Write(seg.GetType().Name.Substring(0, 12) + i);
            Console.Write(" {");
            for (var j = 0; j < seg.Count; j++)
            {
                Console.Write("{0},", seg[j]);
            }
            Console.WriteLine("}");
        }
    }

여러분이해야 할 일은 ArraySegment를 IList로 캐스트하는 것뿐입니다. 그러면 처음부터 예상했던 모든 작업을 수행 할 것입니다. 유형이 일반 목록처럼 작동하더라도 여전히 ArraySegment입니다.

산출:

ArraySegment0 {0,1,2,}
ArraySegment1 {3,4,5,}
ArraySegment2 {6,7,8,}


답변

간단히 말해서, 배열에 대한 참조를 유지하여 각각 다른 범위를 가진 단일 배열 변수에 대한 여러 참조를 가질 수 있습니다.

실제로 시작 인덱스와 길이를 유지하기 위해 여러 변수를 갖는 대신보다 구조화 된 방식으로 배열의 섹션을 사용하고 전달하는 데 도움이됩니다. 또한 배열 섹션으로보다 쉽게 ​​작업 할 수 있도록 컬렉션 인터페이스를 제공합니다.

예를 들어 다음 두 코드 예제는 동일한 작업을 수행합니다. 하나는 ArraySegment를 사용하고 다른 하나는 사용하지 않습니다.

        byte[] arr1 = new byte[] { 1, 2, 3, 4, 5, 6 };
        ArraySegment<byte> seg1 = new ArraySegment<byte>(arr1, 2, 2);
        MessageBox.Show((seg1 as IList<byte>)[0].ToString());

과,

        byte[] arr1 = new byte[] { 1, 2, 3, 4, 5, 6 };
        int offset = 2;
        int length = 2;
        byte[] arr2 = arr1;
        MessageBox.Show(arr2[offset + 0].ToString());

특히 배열 세그먼트를 함수에 전달하려는 경우 첫 번째 코드 스 니펫이 더 선호됩니다.


답변