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


98

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

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


8
ArraySegment.Net 4.5에서 열거 가능한 것처럼 보입니다 .
svick

이 질문 과 같은 시도를 위해 ..
Ken Kin

답변:


57

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

4
설명 할 수 없을 정도로 GetEnumerator비공개로 설정되어 있기 때문에 IEnumerable<T>(권투 변환) 캐스팅 하여 호출해야합니다. 어!
BlueRaja-Danny Pflughoeft

27
  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 구조-그들은 무엇을 생각하고 있었습니까?


3
ArraySegment는 구조 일뿐입니다. 나의 가장 좋은 추측은 그것의 목적은 배열의 세그먼트를 복사하지 않고도 전달할 수 있도록하는 것입니다.
Brian

1
for 루프의 조건문은이어야한다고 생각 i < segment.Offset + segment.Count합니다.
Eren Ersönmez 2011 년

1
당신이 언급 한 사실에 +1하지만 @Eren이 옳습니다 : 그런 세그먼트의 요소를 반복 할 수 없습니다.
Şafak Gür

3
다른 사람의 코드를 사용할 때 일반적으로 속성을 부여하는 것이 적절합니다. 그것은 단지 좋은 매너입니다. 귀하의 예제는 dotnetperls.com/arraysegment 에서 시작되었습니다 .

1
물론, 그들은 당신의 대답에서 그것을 빌 렸습니다. 어떤 경우에 그들은 당신에게 신용을 제공해야합니다. :)

26

배열에 대한 참조를 유지하고 인덱스 범위를 저장하는 것 외에는 아무것도하지 않는 작은 군인 구조체입니다. 약간 위험합니다. 어레이 데이터의 복사본을 만들지 않으며 어떤 식 으로든 어레이를 불변으로 만들거나 불변성에 대한 필요성을 표현하지 않습니다. 보다 일반적인 프로그래밍 패턴은 .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에서 추가 지원.


"그다지 유용하지 않습니다."-불행히도 메모리 제한으로 인해 마이크로 최적화가 필요한 시스템에서 매우 유용하다는 것을 알았습니다. 다른 "일반적인"솔루션 있다는 사실 은 그 유용성을 저하시키지 않습니다
AaronHS

5
좋아, 좋아, 나는 그것을 사용하는 습관을 가진 모든 사람들의 평가가 정말로 필요하지 않습니다. :) @CRice의 의견을 찬성하는 것이 가장 좋습니다. 언급했듯이 "당신이 그것을 좋아한다면 그것을 사용하십시오". 그래서 그것을 사용하십시오. 조각은 멋질 것이고 기다릴 수 없습니다.
Hans Passant

불변의 순수 주의자를위한 ReadOnlySpan이 있습니다.
Arek Bal

7

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

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

매우 유용한 친구, 감사, 노트 당신은 그것을 구현할 수 IEnumerable<T>있는 IEnumerator 다음 추가IEnumerable.GetEnumerator() { return GetEnumerator(); }
마야

5

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,}

4
그것을 캐스트 해야하는 것이 유감 IList<T>입니다. 인덱서가 될 것으로 예상합니다 public.
xmedeko

2
이 답변을 듣고 기적적인 솔루션이라고 생각하는 사람에게는 먼저 성능 요구 사항을 고려하고 어레이 세그먼트의 인덱스 제약 조건을 사용하여 원래 어레이에 직접 액세스하는 것과 비교하여 이것을 벤치마킹하는 것이 좋습니다. IList로 캐스팅하려면 구현에 도달하기 전에 IList 인터페이스를 통해 이동하려면 인덱서를 포함한 후속 메서드 호출이 필요합니다. 사람들이 타이트 루프에서 추상화 된 호출을 사용하는 데 드는 성능 비용에 대해 이야기하는 인터넷에서 많은 토론이 있습니다. 여기에서 읽으십시오 : github.com/dotnet/coreclr/issues/9105
JamesHoux

3

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

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

예를 들어 다음 두 코드 예제는 동일한 작업을 수행합니다. 하나는 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());

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.