2011-01-05 21 views
68

Właśnie natknąłem się na typ ArraySegment<byte> podczas podklasy klasy MessageEncoder.jaki jest pożytek z klasy ArraySegment <T>?

Teraz rozumiem, że jest to segment danej tablicy, przyjmuje przesunięcie, nie jest przeliczalny i nie ma indeksatora, ale nadal nie rozumiem jego użycia. Czy ktoś może wyjaśnić na przykładzie?

+8

Wygląda 'ArraySegment' jest przeliczalna w .NET 4.5. – svick

+0

Do próby podobnej [to pytanie] (https://stackoverflow.com/questions/27965131/how-to-remove-the-first-element-in-an-array) .. –

Odpowiedz

19
  1. partioning Buffer dla klas IO - Użyj tego samego buforu do jednoczesnego operacji odczytu i zapisu i mają jednolitą strukturę można przejść wokół sekcji opisuje całą operację.
  2. Funkcje zestawu - w matematyce można reprezentować ciągłe podzestawy przy użyciu nowej struktury . Oznacza to, że możesz utworzyć partycje tablicy, , ale nie możesz reprezentować wszystkich kursów i wszystkich równości. Zauważ, że telefon Teaser zaproponowany przez The1 mógł być elegancko rozwiązany przy użyciu przy użyciu struktury ArraySegment i drzewa . Ostateczne liczby mogły zostać zapisane przez przechodzenie na początku drzewa. To byłby idealny scenariusz pod względem pamięci i prędkości, którą uważam za .
  3. Wielowątkowość - możesz teraz odradzać wiele wątków, aby działać na tym samym źródle danych o numerze , a jako bramki sterujące używać segmentowanych macierzy . Pętle , które używają obliczeń dyskretnych, teraz mogą być łatwo wyodrębnione, , coś, co , że najnowsze kompilatory C++ to zaczynają jako optymalizacja kodu krok.
  4. Segmentacja interfejsu użytkownika - Ogranicz wyświetlanie interfejsu użytkownika przy użyciu segmentowanych struktur . Można teraz przechowywać struktury reprezentujące strony danych , które można szybko zastosować do funkcji wyświetlania . Pojedyncze ciągłe tablice mogą być używane do wyświetlania widoków dyskretnych, a nawet hierarchicznych struktur , takich jak węzły w TreeView przez segmentowanie magazynu liniowych danych do segmentów kolekcji węzłów.

W tym przykładzie przyjrzymy się w jaki sposób można korzystać z oryginalnej tablicy, przesunięcie i liczyć właściwości, a także w jaki sposób można pętli elementów określonych w 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 Structure - what were they thinking?

+3

ArraySegment to tylko struktura.Domyślam się, że jego celem jest umożliwienie przekazania segmentu tablicy bez konieczności tworzenia kopii. – Brian

+1

Wierzę, że instrukcja warunku pętli for powinna być 'i

+1

+1 dla faktów, o których wspomniałeś, ale @Eren ma rację: nie możesz iterować elementów tego segmentu. –

9

Jest to drobny mały żołnierz struct, że nic nie robi, ale zachować odniesienie do tablicy i zapisuje zakres indeksu. Trochę niebezpiecznie, strzeż się, że nie tworzy kopii danych tablicowych i nie czyni w żaden sposób tablicy niezmienną ani nie wyrażają potrzeby niezmienności. Bardziej typowym wzorcem programistycznym jest po prostu przechowywanie lub przekazywanie tablicy i zmiennej długości lub parametru, tak jak odbywa się to w metodach .NET BeginRead(), String.SubString(), Encoding.GetString(), itp., Itp.

Nie ma dużego zastosowania wewnątrz.NET Framework, z wyjątkiem tego, co wydaje się być jednym konkretnym programistą Microsoftu, który pracował na gniazdach internetowych i WCF lubił to. Które jest prawdopodobnie właściwym przewodnikiem, jeśli ci się podoba, użyj go. Zrobił peek-a-boo w .NET 4.6, dodaje go metoda MemoryStream.TryGetBuffer(). Preferowany jest posiadanie dwóch argumentów out, które zakładam.

Ogólnie rzecz biorąc, bardziej uniwersalne pojęcie plasterków znajduje się wysoko na liście życzeń głównych inżynierów .NET, takich jak Mads Torgersen i Stephen Toub. Ta ostatnia już dawno temu zainicjowała propozycję składni array[:], możesz zobaczyć, o czym oni myśleli w this Roslyn page. Założę się, że uzyskanie wsparcia CLR jest tym, co ostatecznie opiera się na. To jest aktywnie rozważane w C# wersji 7 afaik, miej oko na System.Slices.

Aktualizacja: martwy link, to dostarczane w wersji 7.2 jako Span.

+0

+1 To nie jest cudowna struktura. :) –

+33

Jeśli unika się kosztownej kopii, to nie jest bezużyteczne ... – CRice

+0

"To nie jest zbyt użyteczne" - uznałem, że jest to niezwykle przydatne w systemie, który niestety wymagał mikro optymalizacji ze względu na ograniczenie pamięci. są * także * inne "typowe" rozwiązania nie umniejszają jego użyteczności – AaronHS

34

ArraySegment<T> stał się lot more useful in .NET 4.5 gdyż teraz realizuje:

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

w przeciwieństwie do .NET 4 version, który nie zaimplementował żadnych interfejsów.

Klasa jest teraz w stanie wziąć udział w cudownym świecie LINQ, więc możemy zrobić zwykłe rzeczy LINQ, takie jak zapytanie zawartości, odwrócić zawartość bez wpływu na oryginalną tablicę, uzyskać pierwszy element, i tak dalej:

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 
+0

Chociaż w niewytłumaczalny sposób sprawiły, że 'GetEnumerator' stał się prywatny, co oznacza, że ​​musisz rzucić na' IEnumerable '(konwersja boksu) Aby go nazwać Ugh! –

7

Co to jest klasa opakowania? Aby uniknąć kopiowania danych do tymczasowych buforów.

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 

Przykład:

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 
+0

Bardzo przydatny kumpel, dziękuję, proszę zauważyć, że można go zaimplementować 'IEnumerable ' następnie dodać IEnumerator 'IEnumerable.GetEnumerator() {return GetEnumerato r(); } ' – MaYaN

4

ArraySegment jest znacznie bardziej użyteczne niż myślisz. Spróbuj wykonać następujący test jednostki i przygotuj się na zdumienie!

[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("}"); 
     } 
    } 

Widzisz, wszystko co musisz zrobić, to rzucić ArraySegment do IList i będzie to robić wszystkie rzeczy, które prawdopodobnie spodziewanych to zrobić w pierwszej kolejności. Zauważ, że typem jest nadal ArraySegment, mimo że zachowuje się jak normalna lista.

WYJŚCIE:

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

Szkoda, że ​​trzeba go rzucić na" IList ". Spodziewam się, że indeksator będzie "publiczny". – xmedeko

Powiązane problemy